diff --git a/packages/openchs-android/integrationTest/ReportCardServiceIntegrationTest.js b/packages/openchs-android/integrationTest/ReportCardServiceIntegrationTest.js index 53c6683d1..bcf39fd92 100644 --- a/packages/openchs-android/integrationTest/ReportCardServiceIntegrationTest.js +++ b/packages/openchs-android/integrationTest/ReportCardServiceIntegrationTest.js @@ -1,5 +1,7 @@ import BaseIntegrationTest from "./BaseIntegrationTest"; import { + EncounterType, + Encounter, ApprovalStatus, EntityApprovalStatus, AddressLevel, @@ -38,6 +40,13 @@ import {assert} from "chai"; import General from "../src/utility/General"; import TestEntityApprovalStatusFactory from "../test/model/approval/TestEntityApprovalStatusFactory"; import TestApprovalStatusFactory from "../test/model/approval/TestApprovalStatusFactory"; +import TestEncounterFactory from "../test/model/txn/TestEncounterFactory"; +import TestEncounterTypeFactory from "../test/model/TestEncounterTypeFactory"; +import moment from "moment"; + +function getCount(reportCardService, card, reportFilters) { + return reportCardService.getReportCardCount(card, reportFilters).primaryValue +} class ReportCardServiceIntegrationTest extends BaseIntegrationTest { approvedCard; @@ -54,6 +63,7 @@ class ReportCardServiceIntegrationTest extends BaseIntegrationTest { db.create(Settings, TestSettingsFactory.createWithDefaults({})); this.subjectType = db.create(SubjectType, TestSubjectTypeFactory.createWithDefaults({type: SubjectType.types.Person, name: 'Beneficiary'})); + const encounterType = db.create(EncounterType, TestEncounterTypeFactory.create({name: "Bar"})); const form = db.create(Form, TestFormFactory.createWithDefaults({formType: Form.formTypes.IndividualProfile})); const formElementGroup = db.create(FormElementGroup, TestFormElementGroupFactory.create({form: form})); db.create(FormElement, TestFormElementFactory.create({ @@ -67,34 +77,63 @@ class ReportCardServiceIntegrationTest extends BaseIntegrationTest { db.create(FormMapping, TestFormMappingFactory.createWithDefaults({subjectType: this.subjectType, form: form})); db.create(OrganisationConfig, TestOrganisationConfigFactory.createWithDefaults({})); const approvalStatus = db.create(ApprovalStatus, TestApprovalStatusFactory.create({})); - const subjectUuid = General.randomUUID(); - const entityApprovalStatus = db.create(EntityApprovalStatus, TestEntityApprovalStatusFactory.create({ + const pendingStatus = db.create(ApprovalStatus, TestApprovalStatusFactory.create({status: ApprovalStatus.statuses.Pending})); + const subjectId = General.randomUUID(); + const encounterId = General.randomUUID(); + + const subjectEAS = db.create(EntityApprovalStatus, TestEntityApprovalStatusFactory.create({ entityType: EntityApprovalStatus.entityType.Subject, - entityUUID: subjectUuid, + entityUUID: subjectId, entityTypeUuid: this.subjectType.uuid, approvalStatus: approvalStatus })); - db.create(Individual, TestSubjectFactory.createWithDefaults({ + const encEAS = db.create(EntityApprovalStatus, TestEntityApprovalStatusFactory.create({ + entityType: EntityApprovalStatus.entityType.Encounter, + entityUUID: encounterId, + entityTypeUuid: encounterType.uuid, + approvalStatus: pendingStatus + })); + const subject = db.create(Individual, TestSubjectFactory.createWithDefaults({ subjectType: this.subjectType, address: this.addressLevel, firstName: "foo", lastName: "bar", observations: [TestObsFactory.create({concept: this.concept, valueJSON: JSON.stringify(this.concept.getValueWrapperFor("ABC"))})], - approvalStatuses: [entityApprovalStatus] + approvalStatuses: [subjectEAS] + })); + + db.create(Encounter, TestEncounterFactory.create({ + uuid: encounterId, + earliestVisitDateTime: moment().add(-2, "day").toDate(), + maxVisitDateTime: moment().add(2, "day").toDate(), + encounterType: encounterType, + approvalStatuses: [encEAS], + subject: subject })); const approvedCardType = db.create(StandardReportCardType, TestStandardReportCardTypeFactory.create({name: StandardReportCardType.type.Approved})); - this.approvedCard = db.create(ReportCard, TestReportCardFactory.create({name: "foo", standardReportCardType: approvedCardType})); + const scheduledVisitsCardType = db.create(StandardReportCardType, TestStandardReportCardTypeFactory.create({name: StandardReportCardType.type.ScheduledVisits})); + this.approvedCard = db.create(ReportCard, TestReportCardFactory.create({name: "a", standardReportCardType: approvedCardType})); + this.scheduledVisitsCard = db.create(ReportCard, TestReportCardFactory.create({name: "b", standardReportCardType: scheduledVisitsCardType})); }); } getResultForApprovalCardsType() { const reportCardService = this.getService(ReportCardService); - assert.equal(1, reportCardService.getReportCardCount(this.approvedCard, []).primaryValue); + assert.equal(1, getCount(reportCardService, this.approvedCard, [])); let filterValues = [TestDashboardReportFilterFactory.create({type: CustomFilter.type.Address, filterValue: [this.addressLevel]})]; - assert.equal(1, reportCardService.getReportCardCount(this.approvedCard, filterValues).primaryValue); + assert.equal(1, getCount(reportCardService, this.approvedCard, filterValues)); filterValues = [TestDashboardReportFilterFactory.create({type: CustomFilter.type.Address, filterValue: [this.addressLevel2]})]; - assert.equal(0, reportCardService.getReportCardCount(this.approvedCard, filterValues).primaryValue); + assert.equal(0, getCount(reportCardService, this.approvedCard, filterValues)); + filterValues = [TestDashboardReportFilterFactory.create({type: CustomFilter.type.Address, filterValue: [this.addressLevel, this.addressLevel2]})]; + assert.equal(1, getCount(reportCardService, this.approvedCard, filterValues)); + } + + getCountForDefaultCardsType() { + const reportCardService = this.getService(ReportCardService); + assert.equal(1, getCount(reportCardService, this.scheduledVisitsCard, [])); + assert.equal(1, getCount(reportCardService, this.scheduledVisitsCard, [TestDashboardReportFilterFactory.create({type: CustomFilter.type.Address, filterValue: [this.addressLevel]})])); + assert.equal(0, getCount(reportCardService, this.scheduledVisitsCard, [TestDashboardReportFilterFactory.create({type: CustomFilter.type.Address, filterValue: [this.addressLevel2]})])); } } diff --git a/packages/openchs-android/src/GlobalContext.js b/packages/openchs-android/src/GlobalContext.js index 0c01d849f..596c52352 100644 --- a/packages/openchs-android/src/GlobalContext.js +++ b/packages/openchs-android/src/GlobalContext.js @@ -27,6 +27,7 @@ class GlobalContext { async initialiseGlobalContext(appStore, realmFactory) { this.db = await realmFactory.createRealm(); + this.db.setLogQueries(true); this.beanRegistry.init(this.db); this.reduxStore = appStore.create(this.beanRegistry.beansMap); this.beanRegistry.setReduxStore(this.reduxStore); diff --git a/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js b/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js index 298b2adba..8eb154352 100644 --- a/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js +++ b/packages/openchs-android/src/action/mydashboard/MyDashboardActions.js @@ -10,6 +10,7 @@ import UserInfoService from "../../service/UserInfoService"; import DashboardCacheService from "../../service/DashboardCacheService"; import {firebaseEvents, logEvent} from "../../utility/Analytics"; import LocalCacheService from '../../service/LocalCacheService'; +import RealmQueryService from "../../service/query/RealmQueryService"; function getApplicableEncounterTypes(state) { return _.isEmpty(state.selectedGeneralEncounterTypes) ? state.selectedEncounterTypes : state.selectedGeneralEncounterTypes; @@ -51,10 +52,6 @@ class MyDashboardActions { .reduce((acc, f) => f.compositeFn(acc), individuals); } - static orQuery(array) { - return array.length > 0 ? '( ' + array.join(' OR ') + ' )' : '' - } - static commonIndividuals = (otherFilteredIndividuals, customFilteredIndividualsUUIDs, isTotal = false) => { const getIndividualUUID = (indInfo) => isTotal ? indInfo.uuid : indInfo.individual.uuid; return ((_.isEmpty(customFilteredIndividualsUUIDs) || _.isEmpty(otherFilteredIndividuals)) ? @@ -100,7 +97,7 @@ class MyDashboardActions { allIndividuals, dueChecklist ] = state.returnEmpty ? [[], [], [], [], [], [], [],[]] : (fetchFromDB ? [ - MyDashboardActions.commonIndividuals(individualService.allScheduledVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs), + MyDashboardActions.commonIndividuals(individualService.allScheduledVisitsIn(state.date.value, [], encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs), MyDashboardActions.commonIndividuals(individualService.allOverdueVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs), MyDashboardActions.commonIndividuals(individualService.recentlyCompletedVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs), MyDashboardActions.commonIndividuals(individualService.recentlyRegistered(state.date.value, individualFilters, state.selectedPrograms, getApplicableEncounterTypes(state)), state.individualUUIDs), @@ -283,55 +280,55 @@ class MyDashboardActions { } const restIndividualFilters = [ - MyDashboardActions.orQuery(programQuery('$enrolment.program.uuid')), - MyDashboardActions.orQuery(visitQuery('$enrolment.encounters.encounterType.uuid')), + RealmQueryService.orQuery(programQuery('$enrolment.program.uuid')), + RealmQueryService.orQuery(visitQuery('$enrolment.encounters.encounterType.uuid')), validEnrolmentQuery('$enrolment') ].filter(Boolean).join(" AND "); const buildEnrolmentSubQueryForIndividual = () => _.isEmpty(restIndividualFilters) ? '' : 'SUBQUERY(enrolments, $enrolment, ' + restIndividualFilters + ' ).@count > 0'; - const encounterQuery = () => _.isEmpty(MyDashboardActions.orQuery(generalVisitQueryFromIndividual)) ? '' : - 'SUBQUERY(encounters, $encounter, ' + MyDashboardActions.orQuery(generalVisitQueryFromIndividual) + ' ).@count > 0'; + const encounterQuery = () => _.isEmpty(RealmQueryService.orQuery(generalVisitQueryFromIndividual)) ? '' : + 'SUBQUERY(encounters, $encounter, ' + RealmQueryService.orQuery(generalVisitQueryFromIndividual) + ' ).@count > 0'; const individualFilters = [ subjectTypeQuery('subjectType.uuid'), - MyDashboardActions.orQuery(genderQuery('gender.name')), - MyDashboardActions.orQuery(locationQuery('lowestAddressLevel.uuid')), + RealmQueryService.orQuery(genderQuery('gender.name')), + RealmQueryService.orQuery(locationQuery('lowestAddressLevel.uuid')), encounterQuery(), buildEnrolmentSubQueryForIndividual() ].filter(Boolean).join(" AND "); const encountersFilters = [ subjectTypeQuery('programEnrolment.individual.subjectType.uuid'), - MyDashboardActions.orQuery(genderQuery('programEnrolment.individual.gender.name')), - MyDashboardActions.orQuery(locationQuery('programEnrolment.individual.lowestAddressLevel.uuid')), - MyDashboardActions.orQuery(programQuery('programEnrolment.program.uuid')), - MyDashboardActions.orQuery(visitQuery('encounterType.uuid')), + RealmQueryService.orQuery(genderQuery('programEnrolment.individual.gender.name')), + RealmQueryService.orQuery(locationQuery('programEnrolment.individual.lowestAddressLevel.uuid')), + RealmQueryService.orQuery(programQuery('programEnrolment.program.uuid')), + RealmQueryService.orQuery(visitQuery('encounterType.uuid')), validEnrolmentQuery("programEnrolment") ].filter(Boolean).join(" AND "); const generalEncountersFilters = [ subjectTypeQuery('individual.subjectType.uuid'), - MyDashboardActions.orQuery(genderQuery('individual.gender.name')), - MyDashboardActions.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), - MyDashboardActions.orQuery(generalVisitQuery('encounterType.uuid')) + RealmQueryService.orQuery(genderQuery('individual.gender.name')), + RealmQueryService.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), + RealmQueryService.orQuery(generalVisitQuery('encounterType.uuid')) ].filter(Boolean).join(" AND "); const enrolmentFilters = [ subjectTypeQuery('individual.subjectType.uuid'), - MyDashboardActions.orQuery(genderQuery('individual.gender.name')), - MyDashboardActions.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), - MyDashboardActions.orQuery(programQuery('program.uuid')), - MyDashboardActions.orQuery(visitQuery('encounters.encounterType.uuid')), + RealmQueryService.orQuery(genderQuery('individual.gender.name')), + RealmQueryService.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), + RealmQueryService.orQuery(programQuery('program.uuid')), + RealmQueryService.orQuery(visitQuery('encounters.encounterType.uuid')), 'voided = false and programExitDateTime = null' ].filter(Boolean).join(" AND "); const dueChecklistFilter = [ subjectTypeQuery('individual.subjectType.uuid'), - MyDashboardActions.orQuery(genderQuery('individual.gender.name')), - MyDashboardActions.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), - MyDashboardActions.orQuery(programQuery('program.uuid')), + RealmQueryService.orQuery(genderQuery('individual.gender.name')), + RealmQueryService.orQuery(locationQuery('individual.lowestAddressLevel.uuid')), + RealmQueryService.orQuery(programQuery('program.uuid')), 'programExitDateTime = null' ].filter(Boolean).join(" AND "); diff --git a/packages/openchs-android/src/model/DashboardReportFilters.js b/packages/openchs-android/src/model/DashboardReportFilters.js index d6388c38f..b3cce45c0 100644 --- a/packages/openchs-android/src/model/DashboardReportFilters.js +++ b/packages/openchs-android/src/model/DashboardReportFilters.js @@ -1,5 +1,8 @@ // Objects has been used in place of arrays to allow for flexibility in contract in the future. +import _ from "lodash"; +import {CustomFilter} from "openchs-models"; + export class DashboardReportFilter { type; dataType; @@ -7,6 +10,10 @@ export class DashboardReportFilter { groupSubjectTypeFilter; observationBasedFilter; filterValue; + + static getAddressFilter(reportFilters) { + return _.find(reportFilters, (x: DashboardReportFilter) => x.type === CustomFilter.type.Address); + } } class DashboardReportFilters { diff --git a/packages/openchs-android/src/service/EntityApprovalStatusService.js b/packages/openchs-android/src/service/EntityApprovalStatusService.js index b31b5e15e..b7bd7f9e8 100644 --- a/packages/openchs-android/src/service/EntityApprovalStatusService.js +++ b/packages/openchs-android/src/service/EntityApprovalStatusService.js @@ -16,13 +16,7 @@ import { import _ from 'lodash'; import {DashboardReportFilter} from "../model/DashboardReportFilters"; import AddressLevel from "../views/common/AddressLevel"; - -const locationBasedQueries = new Map(); -locationBasedQueries.set(Individual.schema.name, "lowestAddressLevel.uuid = $0"); -locationBasedQueries.set(ProgramEnrolment.schema.name, "individual.lowestAddressLevel.uuid = $0"); -locationBasedQueries.set(ProgramEncounter.schema.name, "programEnrolment.individual.lowestAddressLevel.uuid = $0"); -locationBasedQueries.set(Encounter.schema.name, "individual.lowestAddressLevel.uuid = $0"); -locationBasedQueries.set(ChecklistItem.schema.name, "checklist.programEnrolment.individual.lowestAddressLevel.uuid = $0"); +import RealmQueryService from "./query/RealmQueryService"; function getEntityApprovalStatuses(service, schema, status) { return service.getAll(schema) @@ -56,12 +50,8 @@ class EntityApprovalStatusService extends BaseService { const applicableEntitiesSchema = EntityApprovalStatus.getApprovalEntitiesSchema(); const result = _.map(applicableEntitiesSchema, (schema) => { let entities = getEntityApprovalStatuses(this, schema, approvalStatus_status); - const addressFilter = _.find(reportFilters, (x: DashboardReportFilter) => x.type === CustomFilter.type.Address); - if (!_.isNil(addressFilter)) { - addressFilter.filterValue.forEach((x: AddressLevel) => { - entities = entities.filtered(locationBasedQueries.get(schema), x.uuid); - }); - } + const addressFilter = DashboardReportFilter.getAddressFilter(reportFilters); + entities = RealmQueryService.filterBasedOnAddress(schema, entities, addressFilter) return {title: schema, data: entities}; }); return {status: approvalStatus_status, result}; diff --git a/packages/openchs-android/src/service/IndividualService.js b/packages/openchs-android/src/service/IndividualService.js index e4675c0a6..e98bca682 100644 --- a/packages/openchs-android/src/service/IndividualService.js +++ b/packages/openchs-android/src/service/IndividualService.js @@ -23,6 +23,8 @@ import EntityApprovalStatusService from "./EntityApprovalStatusService"; import GroupSubjectService from "./GroupSubjectService"; import OrganisationConfigService from './OrganisationConfigService'; import {getUnderlyingRealmCollection} from "openchs-models"; +import RealmQueryService from "./query/RealmQueryService"; +import {DashboardReportFilter} from "../model/DashboardReportFilters"; @Service("individualService") class IndividualService extends BaseService { @@ -156,7 +158,7 @@ class IndividualService extends BaseService { } allInWithFilters = (ignored, queryAdditions, programs = [], encounterTypes = []) => { - if(!this.hideTotalForProgram() || (_.isEmpty(programs) && _.isEmpty(encounterTypes))) { + if (!this.hideTotalForProgram() || (_.isEmpty(programs) && _.isEmpty(encounterTypes))) { return this.allIn(ignored, queryAdditions); } return null; @@ -170,25 +172,33 @@ class IndividualService extends BaseService { .sorted('name'); } - allScheduledVisitsIn(date, programEncounterCriteria, encounterCriteria, queryProgramEncounter = true, queryGeneralEncounter = true) { + allScheduledVisitsIn(date, reportFilters, programEncounterCriteria, encounterCriteria, queryProgramEncounter = true, queryGeneralEncounter = true) { const performProgramVisitCriteria = `privilege.name = '${Privilege.privilegeName.performVisit}' AND privilege.entityType = '${Privilege.privilegeEntityType.encounter}'`; const privilegeService = this.getService(PrivilegeService); const allowedProgramEncounterTypeUuidsForPerformVisit = privilegeService.allowedEntityTypeUUIDListForCriteria(performProgramVisitCriteria, 'programEncounterTypeUuid'); const dateMidnight = moment(date).endOf('day').toDate(); const dateMorning = moment(date).startOf('day').toDate(); - const programEncounters = queryProgramEncounter ? this.db.objects(ProgramEncounter.schema.name) - .filtered('earliestVisitDateTime <= $0 ' + - 'AND maxVisitDateTime >= $1 ' + - 'AND encounterDateTime = null ' + - 'AND cancelDateTime = null ' + - 'AND programEnrolment.programExitDateTime = null ' + - 'AND programEnrolment.voided = false ' + - 'AND programEnrolment.individual.voided = false ' + - 'AND voided = false ', - dateMidnight, - dateMorning) - .filtered((_.isEmpty(programEncounterCriteria) ? 'uuid != null' : `${programEncounterCriteria}`)) - .map((enc) => { + const addressFilter = DashboardReportFilter.getAddressFilter(reportFilters); + + let programEncounters = []; + if (queryProgramEncounter) { + programEncounters = this.db.objects(ProgramEncounter.schema.name) + .filtered('earliestVisitDateTime <= $0 ' + + 'AND maxVisitDateTime >= $1 ' + + 'AND encounterDateTime = null ' + + 'AND cancelDateTime = null ' + + 'AND programEnrolment.programExitDateTime = null ' + + 'AND programEnrolment.voided = false ' + + 'AND programEnrolment.individual.voided = false ' + + 'AND voided = false ', + dateMidnight, + dateMorning); + if (!_.isEmpty(programEncounterCriteria)) + programEncounters = programEncounters.filtered(programEncounterCriteria); + if (!_.isNil(addressFilter)) + programEncounters = RealmQueryService.filterBasedOnAddress(ProgramEncounter.schema.name, programEncounters, addressFilter); + + programEncounters = programEncounters.map((enc) => { const individual = enc.programEnrolment.individual; const visitName = enc.name || enc.encounterType.operationalEncounterTypeName; const programName = enc.programEnrolment.program.operationalProgramName || enc.programEnrolment.program.name; @@ -207,20 +217,27 @@ class IndividualService extends BaseService { allow: !privilegeService.hasEverSyncedGroupPrivileges() || privilegeService.hasAllPrivileges() || _.includes(allowedProgramEncounterTypeUuidsForPerformVisit, enc.encounterType.uuid) } }; - }) : []; + }); + } const allowedGeneralEncounterTypeUuidsForPerformVisit = this.getService(PrivilegeService).allowedEntityTypeUUIDListForCriteria(performProgramVisitCriteria, 'encounterTypeUuid'); - const encounters = queryGeneralEncounter ? this.db.objects(Encounter.schema.name) - .filtered('earliestVisitDateTime <= $0 ' + - 'AND maxVisitDateTime >= $1 ' + - 'AND encounterDateTime = null ' + - 'AND cancelDateTime = null ' + - 'AND individual.voided = false ' + - 'AND voided = false ', - dateMidnight, - dateMorning) - .filtered((_.isEmpty(encounterCriteria) ? 'uuid != null' : `${encounterCriteria}`)) - .map((enc) => { + let encounters = []; + if (queryGeneralEncounter) { + encounters = this.db.objects(Encounter.schema.name) + .filtered('earliestVisitDateTime <= $0 ' + + 'AND maxVisitDateTime >= $1 ' + + 'AND encounterDateTime = null ' + + 'AND cancelDateTime = null ' + + 'AND individual.voided = false ' + + 'AND voided = false ', + dateMidnight, + dateMorning); + if (!_.isEmpty(encounterCriteria)) + encounters = encounters.filtered(encounterCriteria); + if (!_.isNil(addressFilter)) + encounters = RealmQueryService.filterBasedOnAddress(Encounter.schema.name, encounters, addressFilter); + + encounters = encounters.map((enc) => { const individual = enc.individual; const visitName = enc.name || enc.encounterType.operationalEncounterTypeName; const earliestVisitDateTime = enc.earliestVisitDateTime; @@ -238,7 +255,8 @@ class IndividualService extends BaseService { allow: !privilegeService.hasEverSyncedGroupPrivileges() || privilegeService.hasAllPrivileges() || _.includes(allowedGeneralEncounterTypeUuidsForPerformVisit, enc.encounterType.uuid) } }; - }) : []; + }); + } const allEncounters = [... [...programEncounters, ...encounters] .reduce(this._uniqIndividualWithVisitName, new Map()) @@ -247,10 +265,6 @@ class IndividualService extends BaseService { return allEncounters; } - allScheduledVisitsCount() { - return this.allScheduledVisitsIn().length; - } - withScheduledVisits(program, addressLevel, encounterType) { const todayMidnight = moment(new Date()).endOf('day').toDate(); const todayMorning = moment(new Date()).startOf('day').toDate(); @@ -606,7 +620,7 @@ class IndividualService extends BaseService { General.logDebug("Rule-Failure", `Error while saving visit schedule for other subject: ${nsv.subjectUUID}`); const subjectTypeUUID = _.isEmpty(subject) ? individual.subjectType.uuid : subject.subjectType.uuid; this.getService(RuleEvaluationService).saveFailedRules(e, subjectTypeUUID, individual.uuid, - 'VisitSchedule', individual.subjectType.uuid, !_.isEmpty(nsv.programEnrolment) ? 'ProgramEncounter':'Encounter', nsv.uuid); + 'VisitSchedule', individual.subjectType.uuid, !_.isEmpty(nsv.programEnrolment) ? 'ProgramEncounter' : 'Encounter', nsv.uuid); } } else { //not setting nsv.subject here to differentiate these from visits being scheduled for other subjects diff --git a/packages/openchs-android/src/service/customDashboard/ReportCardService.js b/packages/openchs-android/src/service/customDashboard/ReportCardService.js index 5410693c1..1d4624c91 100644 --- a/packages/openchs-android/src/service/customDashboard/ReportCardService.js +++ b/packages/openchs-android/src/service/customDashboard/ReportCardService.js @@ -68,7 +68,7 @@ class ReportCardService extends BaseService { return this.getService(TaskService).getIncompleteTasks(taskTypeType); } - getResultForDefaultCardsType(type) { + getResultForDefaultCardsType(type, reportFilters) { const individualService = this.getService(IndividualService); const typeToMethodMap = new Map([ [StandardReportCardType.type.ScheduledVisits, individualService.allScheduledVisitsIn], @@ -80,14 +80,14 @@ class ReportCardService extends BaseService { [StandardReportCardType.type.DueChecklist, individualService.dueChecklists.individual] ]); const resultFunc = typeToMethodMap.get(type); - const result = type === StandardReportCardType.type.Total ? resultFunc() : resultFunc(new Date()); + const result = type === StandardReportCardType.type.Total ? resultFunc(reportFilters) : resultFunc(new Date(), reportFilters); const sortedResult = type === StandardReportCardType.type.Total ? result : _.orderBy(result, ({visitInfo}) => visitInfo.sortingBy, 'desc'); return {status: type, result: sortedResult} } - getCountForDefaultCardsType(type) { + getCountForDefaultCardsType(type, reportFilters) { return { - primaryValue: this.getResultForDefaultCardsType(type).result.length, + primaryValue: this.getResultForDefaultCardsType(type, reportFilters).result.length, secondaryValue: null, clickable: true }; @@ -116,7 +116,7 @@ class ReportCardService extends BaseService { case standardReportCardType.isApprovalType() : return this.getCountForApprovalCardsType(standardReportCardType.name, reportFilters); case standardReportCardType.isDefaultType() : - return this.getCountForDefaultCardsType(standardReportCardType.name); + return this.getCountForDefaultCardsType(standardReportCardType.name, reportFilters); case standardReportCardType.isCommentType() : return this.getCountForCommentCardType(); case standardReportCardType.isTaskType() : diff --git a/packages/openchs-android/src/service/query/RealmQueryService.js b/packages/openchs-android/src/service/query/RealmQueryService.js new file mode 100644 index 000000000..eda1351a5 --- /dev/null +++ b/packages/openchs-android/src/service/query/RealmQueryService.js @@ -0,0 +1,26 @@ +import {ChecklistItem, Encounter, Individual, ProgramEncounter, ProgramEnrolment} from "openchs-models"; +import _ from "lodash"; +import AddressLevel from "../../views/common/AddressLevel"; + +const locationBasedQueries = new Map(); +locationBasedQueries.set(Individual.schema.name, "lowestAddressLevel.uuid = "); +locationBasedQueries.set(ProgramEnrolment.schema.name, "individual.lowestAddressLevel.uuid = "); +locationBasedQueries.set(ProgramEncounter.schema.name, "programEnrolment.individual.lowestAddressLevel.uuid = "); +locationBasedQueries.set(Encounter.schema.name, "individual.lowestAddressLevel.uuid = "); +locationBasedQueries.set(ChecklistItem.schema.name, "checklist.programEnrolment.individual.lowestAddressLevel.uuid = "); + +class RealmQueryService { + static orQuery(array) { + return array.length > 0 ? '( ' + array.join(' OR ') + ' )' : '' + } + + static filterBasedOnAddress(schema, entitiesResult, addressFilter) { + if (!_.isNil(addressFilter)) { + const joinedQuery = addressFilter.filterValue.map((x: AddressLevel) => locationBasedQueries.get(schema) + `"${x.uuid}"`); + return entitiesResult.filtered(RealmQueryService.orQuery(joinedQuery)); + } + return entitiesResult; + } +} + +export default RealmQueryService; diff --git a/packages/openchs-android/test/model/TestEncounterTypeFactory.js b/packages/openchs-android/test/model/TestEncounterTypeFactory.js new file mode 100644 index 000000000..e609c6905 --- /dev/null +++ b/packages/openchs-android/test/model/TestEncounterTypeFactory.js @@ -0,0 +1,14 @@ +import General from "../../src/utility/General"; +import {EncounterType} from 'openchs-models'; + +class TestEncounterTypeFactory { + static create({uuid = General.randomUUID(), name = General.randomUUID()}) { + const encounterType = new EncounterType(); + encounterType.uuid = uuid; + encounterType.name = name; + encounterType.displayName = name; + return encounterType; + } +} + +export default TestEncounterTypeFactory; diff --git a/packages/openchs-android/test/model/txn/TestEncounterFactory.js b/packages/openchs-android/test/model/txn/TestEncounterFactory.js new file mode 100644 index 000000000..9b9c42454 --- /dev/null +++ b/packages/openchs-android/test/model/txn/TestEncounterFactory.js @@ -0,0 +1,29 @@ +import General from "../../../src/utility/General"; +import {Encounter} from 'openchs-models'; + +class TestEncounterFactory { + static create({ + uuid = General.randomUUID(), + encounterType, + encounterDateTime, + earliestVisitDateTime, + maxVisitDateTime, + subject: subject, + observations = [], + approvalStatuses = [] + }) { + const encounter = new Encounter(); + encounter.uuid = uuid; + encounter.encounterType = encounterType; + encounter.encounterDateTime = encounterDateTime; + encounter.individual = subject; + encounter.observations = observations; + encounter.approvalStatuses = approvalStatuses; + encounter.earliestVisitDateTime = earliestVisitDateTime; + encounter.maxVisitDateTime = maxVisitDateTime; + encounter.setLatestEntityApprovalStatus(encounter.latestEntityApprovalStatus); + return encounter; + } +} + +export default TestEncounterFactory;