diff --git a/src/domain/usecases/DownloadTemplateUseCase.ts b/src/domain/usecases/DownloadTemplateUseCase.ts index b4f06e86..bda3db39 100644 --- a/src/domain/usecases/DownloadTemplateUseCase.ts +++ b/src/domain/usecases/DownloadTemplateUseCase.ts @@ -21,6 +21,7 @@ import { UsersRepository } from "../repositories/UsersRepository"; import { buildAllPossiblePeriods } from "../../webapp/utils/periods"; import { applyFilter } from "../entities/TemplateFilter"; import { DataElementDisaggregationsMappingRepository } from "../repositories/DataElementDisaggregationsMappingRepository"; +import { getCaptureOrgUnitIdsForDataForm } from "./utils/orgUnits"; export interface DownloadTemplateProps { type: DataFormType; @@ -95,12 +96,17 @@ export class DownloadTemplateUseCase implements UseCase { const element = await getElement(api, type, id); const name = element.displayName ?? element.name; + const orgUnitIds = + _.isEmpty(orgUnits) && settings.orgUnitSelection === "import" + ? getCaptureOrgUnitIdsForDataForm(element.organisationUnits, currentUser.orgUnits) + : orgUnits; + async function getGenerateFile(maxTeiRows?: number) { const result = await getElementMetadata({ api, element, downloadRelationships, - orgUnitIds: orgUnits, + orgUnitIds, startDate: startDate?.toDate(), endDate: endDate?.toDate(), populateStartDate: populateStartDate?.toDate(), diff --git a/src/domain/usecases/utils/__tests__/orgUnits.spec.ts b/src/domain/usecases/utils/__tests__/orgUnits.spec.ts new file mode 100644 index 00000000..50dc6a33 --- /dev/null +++ b/src/domain/usecases/utils/__tests__/orgUnits.spec.ts @@ -0,0 +1,67 @@ +import { OrgUnit } from "../../../entities/OrgUnit"; +import { getCaptureOrgUnitIdsForDataForm } from "../orgUnits"; + +function buildOrgUnit(id: string, path: string): OrgUnit { + return { id, path, name: id, level: path.split("/").length - 1 }; +} + +describe("getCaptureOrgUnitIdsForDataForm", () => { + it("returns empty when the data form org unit is a parent of the user org units", () => { + const dataFormOrgUnits = [{ id: "global", path: "/global" }]; + const userOrgUnits = [buildOrgUnit("spain", "/global/spain"), buildOrgUnit("albania", "/global/albania")]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, userOrgUnits); + + expect(result).toEqual([]); + }); + + it("returns the data form org units when the user has a parent org unit", () => { + const dataFormOrgUnits = [ + { id: "spain", path: "/global/spain" }, + { id: "albania", path: "/global/albania" }, + ]; + const userOrgUnits = [buildOrgUnit("global", "/global")]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, userOrgUnits); + + expect(result).toEqual(["spain", "albania"]); + }); + + it("returns the data form org unit when the user has the exact same org unit", () => { + const dataFormOrgUnits = [{ id: "spain", path: "/global/spain" }]; + const userOrgUnits = [buildOrgUnit("spain", "/global/spain")]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, userOrgUnits); + + expect(result).toEqual(["spain"]); + }); + + it("only returns the data form org units the user has access to", () => { + const dataFormOrgUnits = [ + { id: "spain", path: "/global/spain" }, + { id: "france", path: "/global/france" }, + ]; + const userOrgUnits = [buildOrgUnit("spain", "/global/spain")]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, userOrgUnits); + + expect(result).toEqual(["spain"]); + }); + + it("does not match org units with similar path prefixes that are not ancestors", () => { + const dataFormOrgUnits = [{ id: "spain2", path: "/global/spain2" }]; + const userOrgUnits = [buildOrgUnit("spain", "/global/spain")]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, userOrgUnits); + + expect(result).toEqual([]); + }); + + it("returns empty when the user has no org units", () => { + const dataFormOrgUnits = [{ id: "spain", path: "/global/spain" }]; + + const result = getCaptureOrgUnitIdsForDataForm(dataFormOrgUnits, []); + + expect(result).toEqual([]); + }); +}); diff --git a/src/domain/usecases/utils/orgUnits.ts b/src/domain/usecases/utils/orgUnits.ts new file mode 100644 index 00000000..aba4be59 --- /dev/null +++ b/src/domain/usecases/utils/orgUnits.ts @@ -0,0 +1,19 @@ +import { OrgUnit } from "../../entities/OrgUnit"; +import { Id, Ref } from "../../entities/ReferenceObject"; + +/* Return the org units of the data form (dataSet/program) the current user can capture data for, + either directly or through a parent org unit (matched by path). */ +export function getCaptureOrgUnitIdsForDataForm( + dataFormOrgUnits: Array, + userOrgUnits: OrgUnit[] +): Id[] { + return dataFormOrgUnits + .filter(dataFormOrgUnit => + userOrgUnits.some( + userOrgUnit => + dataFormOrgUnit.path === userOrgUnit.path || + dataFormOrgUnit.path.startsWith(`${userOrgUnit.path}/`) + ) + ) + .map(orgUnit => orgUnit.id); +} diff --git a/src/webapp/components/template-selector/TemplateSelector.tsx b/src/webapp/components/template-selector/TemplateSelector.tsx index 9fc4a918..ecc755dd 100644 --- a/src/webapp/components/template-selector/TemplateSelector.tsx +++ b/src/webapp/components/template-selector/TemplateSelector.tsx @@ -77,7 +77,7 @@ export const TemplateSelector = ({ { startDate: moment().add("-1", "year").startOf("year"), endDate: moment().add("-1", "year").endOf("year"), - relationshipsOuFilter: "SELECTED", + relationshipsOuFilter: settings.orgUnitSelection === "import" ? "CAPTURE" : "SELECTED", populate: false, downloadRelationships: true, filterTEIEnrollmentDate: false, diff --git a/src/webapp/pages/download-template/DownloadTemplatePage.tsx b/src/webapp/pages/download-template/DownloadTemplatePage.tsx index f983004f..ec629730 100644 --- a/src/webapp/pages/download-template/DownloadTemplatePage.tsx +++ b/src/webapp/pages/download-template/DownloadTemplatePage.tsx @@ -30,6 +30,8 @@ export default function DownloadTemplatePage({ settings, themes, customTemplates template; if ( + type === "trackerPrograms" && + populate && (template.relationshipsOuFilter === "SELECTED" || template.relationshipsOuFilter === "CHILDREN" || template.relationshipsOuFilter === "DESCENDANTS") &&