Skip to content

Commit

Permalink
O3-4043: Dispensing: Allow back dating dispense events for retrospect…
Browse files Browse the repository at this point in the history
…ive entry
  • Loading branch information
mogoodrich committed Oct 1, 2024
1 parent 35c1e00 commit fa187f5
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 29 deletions.
7 changes: 7 additions & 0 deletions src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export const configSchema = {
_default: false,
},
},
dispenserProviderRoles: {
_type: Type.String,
_description:
'Comma-separated list of provider roles. 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 Dispense, it does not limit who can create dispense events.',
_default: '',
},
medicationRequestExpirationPeriodInDays: {
_type: Type.Number,
_description: 'Medication Requests older that this will be considered expired',
Expand Down Expand Up @@ -144,6 +150,7 @@ export interface PharmacyConfig {
allowModifyingPrescription: boolean;
restrictTotalQuantityDispensed: boolean;
};
dispenserProviderRoles: string;
medicationRequestExpirationPeriodInDays: number;
locationBehavior: {
locationColumn: {
Expand Down
60 changes: 34 additions & 26 deletions src/forms/medication-dispense-review.component.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ComboBox, DatePicker, DatePickerInput, Dropdown, NumberInput, Stack, TextArea } from '@carbon/react';
import { useLayoutType, useConfig, useSession, userHasAccess } from '@openmrs/esm-framework';
import { getConceptCodingUuid, getMedicationReferenceOrCodeableConcept, getOpenMRSMedicineDrugName } from '../utils';
import { ComboBox, DatePickerInput, Dropdown, NumberInput, Stack, TextArea } from '@carbon/react';
import { OpenmrsDatePicker, useLayoutType, useConfig, useSession, userHasAccess } from '@openmrs/esm-framework';
import {
getConceptCodingUuid,
getMedicationReferenceOrCodeableConcept,
getOpenMRSMedicineDrugName,
isSameDay,
} from '../utils';
import MedicationCard from '../components/medication-card.component';
import { useMedicationCodeableConcept, useMedicationFormulations } from '../medication/medication.resource';
import { useMedicationRequest, usePrescriptionDetails } from '../medication-request/medication-request.resource';
Expand All @@ -12,7 +17,7 @@ import {
useSubstitutionReasonValueSet,
useSubstitutionTypeValueSet,
} from '../medication-dispense/medication-dispense.resource';
import { datePickerFormat, PRIVILEGE_CREATE_DISPENSE_MODIFY_DETAILS } from '../constants';
import { PRIVILEGE_CREATE_DISPENSE_MODIFY_DETAILS } from '../constants';
import { type Medication, type MedicationDispense } from '../types';
import { type PharmacyConfig } from '../config-schema';
import styles from '../components/medication-dispense-review.scss';
Expand Down Expand Up @@ -50,12 +55,26 @@ const MedicationDispenseReview: React.FC<MedicationDispenseReviewProps> = ({

const isTablet = useLayoutType() === 'tablet';

const allowEditing = config.dispenseBehavior.allowModifyingPrescription;
const providerRoles = config.dispenserProviderRoles;

const { orderConfigObject } = useOrderConfig();
const { substitutionTypeValueSet } = useSubstitutionTypeValueSet(config.valueSets.substitutionType.uuid);
const { substitutionReasonValueSet } = useSubstitutionReasonValueSet(config.valueSets.substitutionReason.uuid);
const providers = useProviders();
const providers = useProviders(providerRoles);

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) {
Expand Down Expand Up @@ -148,19 +167,6 @@ const MedicationDispenseReview: React.FC<MedicationDispenseReviewProps> = ({
: 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,
);

// we fetch this just to get the prescription date
const { prescriptionDate } = usePrescriptionDetails(medicationRequest ? medicationRequest.encounter.reference : null);

// check to see if the current dispense would be a substitution, and update accordingly
useEffect(() => {
if (
Expand Down Expand Up @@ -496,20 +502,22 @@ const MedicationDispenseReview: React.FC<MedicationDispenseReviewProps> = ({
}}
/>

<DatePicker
datePickerType="single"
dateFormat={datePickerFormat}
minDate={dayjs(prescriptionDate).startOf('day').toDate()}
<OpenmrsDatePicker
id="dispenseDate"
minDate={prescriptionDate ? dayjs(prescriptionDate).startOf('day').toDate() : null}
maxDate={dayjs().toDate()}
onChange={([date]) => {
onChange={(selectedDate) => {
const currentDate = dayjs(medicationDispense.whenHandedOver);
updateMedicationDispense({
...medicationDispense,
whenHandedOver: dayjs(date).format(),
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()}>
<DatePickerInput id="dispenseDate" labelText={t('dispenseDate', 'Date of Dispense')}></DatePickerInput>
</DatePicker>
</OpenmrsDatePicker>

<ComboBox
id="dispenser"
Expand Down
1 change: 1 addition & 0 deletions src/location/location.resource.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const pharmacyConfig: PharmacyConfig = {
allowModifyingPrescription: false,
restrictTotalQuantityDispensed: false,
},
dispenserProviderRoles: '',
locationBehavior: {
locationColumn: { enabled: false },
locationFilter: { enabled: false, tag: 'Login Location' },
Expand Down
7 changes: 5 additions & 2 deletions src/medication-dispense/medication-dispense.resource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ export function useOrderConfig() {
};
}

export function useProviders() {
export function useProviders(providerRoles: string) {
const rep = 'custom:(uuid,person:(display)';
const { data } = useSWR<{ data: ProviderRequestResponse }, Error>(
`${restBaseUrl}/provider?v=custom:(uuid,person:(display))`,
providerRoles
? `${restBaseUrl}/provider?providerRoles=${providerRoles}&v=${rep})`
: `${restBaseUrl}/provider?v=${rep})`,
openmrsFetch,
);
return data?.data?.results.sort((a, b) => a.person?.display.localeCompare(b.person?.display));
Expand Down
17 changes: 16 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { mutate } from 'swr';
import { type CalendarDate } from '@internationalized/date';
import {
type Coding,
type DosageInstruction,
Expand All @@ -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. " +
Expand Down Expand Up @@ -590,3 +591,17 @@ export function sortMedicationDispensesByWhenHandedOver(a: MedicationDispense, b
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
);
}

0 comments on commit fa187f5

Please sign in to comment.