Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
feat(patients): use randomly generated code instead of sequence
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Any patient saved with friendly id will no longer map friendly id to the patient
object, thus not displaying it in the UI or have it available for search

fix #1876
  • Loading branch information
jackcmeyer committed Mar 4, 2020
1 parent 0d26e82 commit 1d6047d
Show file tree
Hide file tree
Showing 15 changed files with 43 additions and 74 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"react-scripts": "3.4.0",
"redux": "~4.0.5",
"redux-thunk": "~2.3.0",
"shortid": "^2.2.15",
"typescript": "~3.8.2"
},
"repository": {
Expand Down Expand Up @@ -56,6 +57,7 @@
"@types/react-router": "~5.1.2",
"@types/react-router-dom": "~5.1.0",
"@types/redux-mock-store": "~1.0.1",
"@types/shortid": "^0.0.29",
"@typescript-eslint/eslint-plugin": "~2.22.0",
"@typescript-eslint/parser": "~2.22.0",
"commitizen": "~4.0.3",
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/HospitalRun.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('HospitalRun', () => {
givenName: 'test',
familyName: 'test',
suffix: 'test',
friendlyId: 'P00001',
code: 'P00001',
} as Patient

mockedPatientRepository.find.mockResolvedValue(patient)
Expand Down Expand Up @@ -163,7 +163,7 @@ describe('HospitalRun', () => {
givenName: 'test',
familyName: 'test',
suffix: 'test',
friendlyId: 'P00001',
code: 'P00001',
} as Patient

mockedPatientRepository.find.mockResolvedValue(patient)
Expand Down
39 changes: 15 additions & 24 deletions src/__tests__/clients/db/PatientRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { patients } from 'config/pouchdb'
import PatientRepository from 'clients/db/PatientRepository'
import Patient from 'model/Patient'
import { fromUnixTime } from 'date-fns'
import * as shortid from 'shortid'

async function removeAllDocs() {
// eslint-disable-next-line
Expand Down Expand Up @@ -36,22 +37,22 @@ describe('patient repository', () => {
await removeAllDocs()
})

it('should return all records that friendly ids match search text', async () => {
// same full name to prove that it is finding by friendly id
const expectedFriendlyId = 'P00001'
await patients.put({ _id: 'someId1', friendlyId: expectedFriendlyId, fullName: 'test test' })
await patients.put({ _id: 'someId2', friendlyId: 'P00002', fullName: 'test test' })
it('should return all records that patient code matches search text', async () => {
// same full name to prove that it is finding by patient code
const expectedPatientCode = 'P00001'
await patients.put({ _id: 'someId1', code: expectedPatientCode, fullName: 'test test' })
await patients.put({ _id: 'someId2', code: 'P00002', fullName: 'test test' })

const result = await PatientRepository.search(expectedFriendlyId)
const result = await PatientRepository.search(expectedPatientCode)

expect(result).toHaveLength(1)
expect(result[0].friendlyId).toEqual(expectedFriendlyId)
expect(result[0].code).toEqual(expectedPatientCode)
})

it('should return all records that fullName contains search text', async () => {
await patients.put({ _id: 'id3333', friendlyId: 'P00002', fullName: 'blh test test blah' })
await patients.put({ _id: 'id4444', friendlyId: 'P00001', fullName: 'test test' })
await patients.put({ _id: 'id5555', friendlyId: 'P00003', fullName: 'not found' })
await patients.put({ _id: 'id3333', code: 'P00002', fullName: 'blh test test blah' })
await patients.put({ _id: 'id4444', code: 'P00001', fullName: 'test test' })
await patients.put({ _id: 'id5555', code: 'P00003', fullName: 'not found' })

const result = await PatientRepository.search('test test')

Expand All @@ -61,8 +62,8 @@ describe('patient repository', () => {
})

it('should match search criteria with case insensitive match', async () => {
await patients.put({ _id: 'id6666', friendlyId: 'P00001', fullName: 'test test' })
await patients.put({ _id: 'id7777', friendlyId: 'P00002', fullName: 'not found' })
await patients.put({ _id: 'id6666', code: 'P00001', fullName: 'test test' })
await patients.put({ _id: 'id7777', code: 'P00002', fullName: 'not found' })

const result = await PatientRepository.search('TEST TEST')

Expand Down Expand Up @@ -100,22 +101,12 @@ describe('patient repository', () => {
expect(fromUnixTime(parseInt(newPatient.id, 10)).getTime() > 0).toBeTruthy()
})

it('should generate a friendly id', async () => {
it('should generate a patient code', async () => {
const newPatient = await PatientRepository.save({
fullName: 'test1 test1',
} as Patient)

expect(newPatient.friendlyId).toEqual('P00001')
})

it('should sequentially generate a friendly id', async () => {
await patients.put({ _id: 'id9999', friendlyId: 'P00001' })

const newPatient = await PatientRepository.save({
fullName: 'test3 test3',
} as Patient)

expect(newPatient.friendlyId).toEqual('P00002')
expect(shortid.isValid(newPatient.code)).toBeTruthy()
})
})

Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/patients/GeneralInformation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('General Information, without isEditable', () => {
phoneNumber: 'phoneNumber',
email: '[email protected]',
address: 'address',
friendlyId: 'P00001',
code: 'P00001',
dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(),
isApproximateDateOfBirth: false,
} as Patient
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('General Information, isEditable', () => {
phoneNumber: 'phoneNumber',
email: '[email protected]',
address: 'address',
friendlyId: 'P00001',
code: 'P00001',
dateOfBirth: startOfDay(subYears(new Date(), 30)).toISOString(),
isApproximateDateOfBirth: false,
} as Patient
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/patients/edit/EditPatient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('Edit Patient', () => {
phoneNumber: 'phoneNumber',
email: '[email protected]',
address: 'address',
friendlyId: 'P00001',
code: 'P00001',
dateOfBirth: new Date().toISOString(),
} as Patient

Expand Down
6 changes: 2 additions & 4 deletions src/__tests__/patients/list/Patients.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const middlewares = [thunk]
const mockStore = configureStore(middlewares)

describe('Patients', () => {
const patients = [{ id: '123', fullName: 'test test', friendlyId: 'P12345' }]
const patients = [{ id: '123', fullName: 'test test', code: 'P12345' }]
const mockedPatientRepository = mocked(PatientRepository, true)

const setup = (isLoading?: boolean) => {
Expand Down Expand Up @@ -67,9 +67,7 @@ describe('Patients', () => {

const patientListItems = wrapper.find(ListItem)
expect(patientListItems).toHaveLength(1)
expect(patientListItems.at(0).text()).toEqual(
`${patients[0].fullName} (${patients[0].friendlyId})`,
)
expect(patientListItems.at(0).text()).toEqual(`${patients[0].fullName} (${patients[0].code})`)
})

it('should add a "New Patient" button to the button tool bar', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/patients/view/ViewPatient.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ describe('ViewPatient', () => {
phoneNumber: 'phoneNumber',
email: '[email protected]',
address: 'address',
friendlyId: 'P00001',
code: 'P00001',
dateOfBirth: new Date().toISOString(),
} as Patient

Expand Down Expand Up @@ -90,7 +90,7 @@ describe('ViewPatient', () => {
await setup()
})
expect(titleUtil.default).toHaveBeenCalledWith(
`${patient.givenName} ${patient.familyName} ${patient.suffix} (${patient.friendlyId})`,
`${patient.givenName} ${patient.familyName} ${patient.suffix} (${patient.code})`,
)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Edit Appointment', () => {
phoneNumber: 'phoneNumber',
email: '[email protected]',
address: 'address',
friendlyId: 'P00001',
code: 'P00001',
dateOfBirth: new Date().toISOString(),
} as Patient

Expand Down
32 changes: 6 additions & 26 deletions src/clients/db/PatientRepository.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import * as shortid from 'shortid'
import Patient from '../../model/Patient'
import Repository from './Repository'
import { patients } from '../../config/pouchdb'

const formatFriendlyId = (prefix: string, sequenceNumber: string) => `${prefix}${sequenceNumber}`
const formatPatientCode = (prefix: string, sequenceNumber: string) => `${prefix}${sequenceNumber}`

const generateSequenceNumber = (currentNumber: number): string => {
const newNumber = currentNumber + 1
if (newNumber < 10000) {
return newNumber.toString().padStart(5, '0')
}

return newNumber.toString()
}
const getPatientCode = (): string => formatPatientCode('P-', shortid.generate())

export class PatientRepository extends Repository<Patient> {
constructor() {
Expand All @@ -28,30 +22,16 @@ export class PatientRepository extends Repository<Patient> {
},
},
{
friendlyId: text,
code: text,
},
],
},
})
}

async getFriendlyId(): Promise<string> {
const storedPatients = await this.findAll()

if (storedPatients.length === 0) {
return formatFriendlyId('P', generateSequenceNumber(0))
}

const maxPatient = storedPatients[storedPatients.length - 1]
const { friendlyId } = maxPatient
const currentSequenceNumber = friendlyId.slice(1, friendlyId.length)

return formatFriendlyId('P', generateSequenceNumber(parseInt(currentSequenceNumber, 10)))
}

async save(entity: Patient): Promise<Patient> {
const friendlyId = await this.getFriendlyId()
entity.friendlyId = friendlyId
const patientCode = getPatientCode()
entity.code = patientCode
return super.save(entity)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/model/Patient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default interface Patient extends AbstractDBModel, Name, ContactInformati
preferredLanguage?: string
occupation?: string
type?: string
friendlyId: string
code: string
relatedPersons?: RelatedPerson[]
allergies?: Allergy[]
diagnoses?: Diagnosis[]
Expand Down
6 changes: 3 additions & 3 deletions src/patients/edit/EditPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { RootState } from '../../store'
import { getPatientFullName, getPatientName } from '../util/patient-name-util'
import useAddBreadcrumbs from '../../breadcrumbs/useAddBreadcrumbs'

const getFriendlyId = (p: Patient): string => {
const getPatientCode = (p: Patient): string => {
if (p) {
return p.friendlyId
return p.code
}

return ''
Expand All @@ -30,7 +30,7 @@ const EditPatient = () => {
const { patient: reduxPatient, isLoading } = useSelector((state: RootState) => state.patient)

useTitle(
`${t('patients.editPatient')}: ${getPatientFullName(reduxPatient)} (${getFriendlyId(
`${t('patients.editPatient')}: ${getPatientFullName(reduxPatient)} (${getPatientCode(
reduxPatient,
)})`,
)
Expand Down
2 changes: 1 addition & 1 deletion src/patients/list/Patients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const Patients = () => {
<ul>
{patients.map((p) => (
<ListItem action key={p.id} onClick={() => history.push(`/patients/${p.id}`)}>
{p.fullName} ({p.friendlyId})
{p.fullName} ({p.code})
</ListItem>
))}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion src/patients/related-persons/NewRelatedPersonModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const NewRelatedPersonModal = (props: Props) => {
onChange={onPatientSelect}
onSearch={async (query: string) => PatientRepository.search(query)}
renderMenuItemChildren={(patient: Patient) => (
<div>{`${patient.fullName} (${patient.friendlyId})`}</div>
<div>{`${patient.fullName} (${patient.code})`}</div>
)}
/>
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/patients/view/ViewPatient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import RelatedPerson from '../related-persons/RelatedPersonTab'
import useAddBreadcrumbs from '../../breadcrumbs/useAddBreadcrumbs'
import AppointmentsList from '../appointments/AppointmentsList'

const getFriendlyId = (p: Patient): string => {
const getPatientCode = (p: Patient): string => {
if (p) {
return p.friendlyId
return p.code
}

return ''
Expand All @@ -35,7 +35,7 @@ const ViewPatient = () => {
const { patient, isLoading } = useSelector((state: RootState) => state.patient)
const { permissions } = useSelector((state: RootState) => state.user)

useTitle(`${getPatientFullName(patient)} (${getFriendlyId(patient)})`)
useTitle(`${getPatientFullName(patient)} (${getPatientCode(patient)})`)

const setButtonToolBar = useButtonToolbarSetter()

Expand Down
4 changes: 1 addition & 3 deletions src/scheduling/appointments/AppointmentDetailForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,7 @@ const AppointmentDetailForm = (props: Props) => {
onChange={(p: Patient[]) => onFieldChange && onFieldChange('patientId', p[0].id)}
onSearch={async (query: string) => PatientRepository.search(query)}
searchAccessor="fullName"
renderMenuItemChildren={(p: Patient) => (
<div>{`${p.fullName} (${p.friendlyId})`}</div>
)}
renderMenuItemChildren={(p: Patient) => <div>{`${p.fullName} (${p.code})`}</div>}
/>
</div>
</div>
Expand Down

0 comments on commit 1d6047d

Please sign in to comment.