Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [DHIS2-9661] first stage registration #3217

Closed
wants to merge 9 commits into from
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-03-21T20:34:52.414Z\n"
"PO-Revision-Date: 2023-03-21T20:34:52.414Z\n"
"POT-Creation-Date: 2023-03-23T08:32:12.864Z\n"
"PO-Revision-Date: 2023-03-23T08:32:12.864Z\n"

msgid "Choose one or more dates..."
msgstr "Choose one or more dates..."
Expand Down Expand Up @@ -373,6 +373,9 @@ msgstr "Some operations are still runnning. Please wait.."
msgid "Operations running"
msgstr "Operations running"

msgid "Data Entry ({{ stageName }})"
msgstr "Data Entry ({{ stageName }})"

msgid "Sort"
msgstr "Sort"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import i18n from '@dhis2/d2-i18n';
import moment from 'moment';
import { type OrgUnit } from '@dhis2/rules-engine-javascript';
import {
DataEntry,
placements,
withDataEntryField,
withDataEntryFieldIfApplicable,
Expand Down Expand Up @@ -33,7 +32,8 @@ import {
getIncidentDateValidatorContainer,
} from './fieldValidators';
import { sectionKeysForEnrollmentDataEntry } from './constants/sectionKeys.const';
import { type Enrollment } from '../../../metaData';
import { type Enrollment, ProgramStage } from '../../../metaData';
import { FirstStageRegistrationContainer } from '../../DataEntryDhis2Helpers/FirstStageRegistration/FirstStageRegistration.container';

const overrideMessagePropNames = {
errorMessage: 'validationError',
Expand Down Expand Up @@ -251,6 +251,9 @@ const getGeometrySettings = () => ({
type FinalTeiDataEntryProps = {
enrollmentMetadata: Enrollment,
programId: string,
firstStageMetaData?: {
stage: ProgramStage,
}
};
// final step before the generic dataEntry is inserted
class FinalEnrollmentDataEntry extends React.Component<FinalTeiDataEntryProps> {
Expand All @@ -266,10 +269,14 @@ class FinalEnrollmentDataEntry extends React.Component<FinalTeiDataEntryProps> {
};

render() {
const { enrollmentMetadata, programId, ...passOnProps } = this.props;
const {
enrollmentMetadata,
...passOnProps
} = this.props;

return (
// $FlowFixMe[cannot-spread-inexact] automated comment
<DataEntry
<FirstStageRegistrationContainer
{...passOnProps}
dataEntrySections={FinalEnrollmentDataEntry.dataEntrySectionDefinitions}
formFoundation={enrollmentMetadata.enrollmentForm}
Expand All @@ -291,6 +298,9 @@ type PreEnrollmentDataEntryProps = {
onStartAsyncUpdateField: Function,
onGetUnsavedAttributeValues?: ?Function,
teiId?: ?string,
firstStageMetaData?: {
stage: ProgramStage,
}
};

class PreEnrollmentDataEntryPure extends React.PureComponent<Object> {
Expand All @@ -315,30 +325,30 @@ export class EnrollmentDataEntryComponent extends React.Component<PreEnrollmentD
}

handleUpdateField = (...args: Array<any>) => {
const { programId, orgUnit } = this.props;
this.props.onUpdateField(...args, programId, orgUnit);
const { programId, orgUnit, firstStageMetaData } = this.props;
this.props.onUpdateField(...args, programId, orgUnit, firstStageMetaData?.stage);
}

handleUpdateDataEntryField = (...args: Array<any>) => {
const { programId, orgUnit } = this.props;
this.props.onUpdateDataEntryField(...args, programId, orgUnit);
const { programId, orgUnit, firstStageMetaData } = this.props;
this.props.onUpdateDataEntryField(...args, programId, orgUnit, firstStageMetaData?.stage);
}

handleStartAsyncUpdateField = (...args: Array<any>) => {
const { programId, orgUnit } = this.props;
this.props.onStartAsyncUpdateField(...args, programId, orgUnit);
const { programId, orgUnit, firstStageMetaData } = this.props;
this.props.onStartAsyncUpdateField(...args, programId, orgUnit, firstStageMetaData?.stage);
}

render() {
const {
orgUnit,
programId,
onUpdateField,
onUpdateDataEntryField,
onStartAsyncUpdateField,
onGetUnsavedAttributeValues,
...passOnProps
} = this.props;

return (
// $FlowFixMe[cannot-spread-inexact] automated comment
<PreEnrollmentDataEntryPure
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import type { OrgUnit } from '@dhis2/rules-engine-javascript';
import { connect } from 'react-redux';
import { ProgramStage } from '../../../metaData';
import { updateFieldBatch, asyncUpdateSuccessBatch, updateDataEntryFieldBatch } from './actions/enrollment.actionBatchs';
import { startAsyncUpdateFieldForNewEnrollment } from './actions/enrollment.actions';
import { EnrollmentDataEntryComponent } from './EnrollmentDataEntry.component';
Expand All @@ -13,25 +14,28 @@ const mapDispatchToProps = (dispatch: ReduxDispatch) => ({
data: any,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
dispatch(updateDataEntryFieldBatch(innerAction, programId, orgUnit));
dispatch(updateDataEntryFieldBatch(innerAction, programId, orgUnit, stage));
},
onUpdateField: (
innerAction: ReduxAction<any, any>,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
dispatch(updateFieldBatch(innerAction, programId, orgUnit));
dispatch(updateFieldBatch(innerAction, programId, orgUnit, stage));
},
onStartAsyncUpdateField: (
innerAction: ReduxAction<any, any>,
dataEntryId: string,
itemId: string,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
const onAsyncUpdateSuccess = (successInnerAction: ReduxAction<any, any>) =>
asyncUpdateSuccessBatch(successInnerAction, dataEntryId, itemId, programId, orgUnit);
asyncUpdateSuccessBatch(successInnerAction, dataEntryId, itemId, programId, orgUnit, stage);
const onAsyncUpdateError = (errorInnerAction: ReduxAction<any, any>) => errorInnerAction;

dispatch(startAsyncUpdateFieldForNewEnrollment(innerAction, onAsyncUpdateSuccess, onAsyncUpdateError));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,39 @@ import type {
} from '@dhis2/rules-engine-javascript';
import { getApplicableRuleEffectsForTrackerProgram, updateRulesEffects } from '../../../../rules';
import { rulesExecutedPostUpdateField } from '../../../DataEntry/actions/dataEntry.actions';
import type { TrackerProgram, RenderFoundation } from '../../../../metaData';
import { TrackerProgram, RenderFoundation, Section, ProgramStage } from '../../../../metaData';
import { startRunRulesPostUpdateField } from '../../../DataEntry';
import { startRunRulesOnUpdateForNewEnrollment } from './enrollment.actions';
import { convertValue } from '../../../../converters/formToClient';

export const batchActionTypes = {
RULES_EXECUTED_POST_UPDATE_FIELD_FOR_ENROLLMENT: 'RulesExecutedPostUpdateFieldForEnrollment',
UPDATE_FIELD_NEW_ENROLLMENT_ACTION_BATCH: 'UpdateFieldNewEnrollmentActionBatch',
UPDATE_DATA_ENTRY_FIELD_NEW_ENROLLMENT_ACTION_BATCH: 'UpdateDataEntryFieldNewEnrollmentActionBatch',
};

export const getCurrentEventValuesFromStage = (attributeValues?: TEIValues, stage?: ProgramStage) => {
const currentEventValues = {};
if (!stage) {
return { currentEventValues, attributeValues };
}

const section = stage.stageForm.getSection(Section.MAIN_SECTION_ID);
const dataElements = [...section.elements.entries()].map(([key, val]) => ({ id: key, type: val.type }));

if (attributeValues) {
Object.keys(attributeValues).forEach((attributeId) => {
const found = dataElements.find(element => element.id === attributeId);
if (found) {
currentEventValues[attributeId] = convertValue(attributeValues[attributeId], found.type);
delete attributeValues[attributeId];
}
});
}

return { currentEventValues, attributeValues };
};

export const runRulesOnUpdateFieldBatch = (
program: TrackerProgram,
foundation: RenderFoundation,
Expand All @@ -26,17 +49,33 @@ export const runRulesOnUpdateFieldBatch = (
itemId: string,
orgUnit: OrgUnit,
enrollmentData?: Enrollment,
attributeValues?: TEIValues,
teiAttributeValues?: TEIValues,
extraActions: Array<ReduxAction<any, any>> = [],
uid: string,
stage?: ProgramStage,
) => {
const effects = getApplicableRuleEffectsForTrackerProgram({
program,
orgUnit,
enrollmentData,
attributeValues,
});
let effects;

if (stage) {
const { attributeValues, currentEventValues } = getCurrentEventValuesFromStage(teiAttributeValues, stage);
const currentEvent = { ...currentEventValues, programStageId: stage.id };
effects = getApplicableRuleEffectsForTrackerProgram({
program,
stage,
orgUnit,
currentEvent,
enrollmentData,
attributeValues,
});
} else {
effects = getApplicableRuleEffectsForTrackerProgram({
program,
stage,
orgUnit,
enrollmentData,
attributeValues: teiAttributeValues,
});
}
return batchActions([
updateRulesEffects(effects, formId),
rulesExecutedPostUpdateField(dataEntryId, itemId, uid),
Expand All @@ -48,29 +87,31 @@ export const updateDataEntryFieldBatch = (
innerAction: ReduxAction<any, any>,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
const { dataEntryId, itemId } = innerAction.payload;
const uid = uuid();

return batchActions([
innerAction,
startRunRulesPostUpdateField(dataEntryId, itemId, uid),
startRunRulesOnUpdateForNewEnrollment(innerAction.payload, uid, programId, orgUnit),
startRunRulesPostUpdateField(dataEntryId, itemId, uid, stage),
startRunRulesOnUpdateForNewEnrollment(innerAction.payload, uid, programId, orgUnit, stage),
], batchActionTypes.UPDATE_DATA_ENTRY_FIELD_NEW_ENROLLMENT_ACTION_BATCH);
};

export const updateFieldBatch = (
innerAction: ReduxAction<any, any>,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
const { dataEntryId, itemId } = innerAction.payload;
const uid = uuid();

return batchActions([
innerAction,
startRunRulesPostUpdateField(dataEntryId, itemId, uid),
startRunRulesOnUpdateForNewEnrollment(innerAction.payload, uid, programId, orgUnit),
startRunRulesPostUpdateField(dataEntryId, itemId, uid, stage),
startRunRulesOnUpdateForNewEnrollment(innerAction.payload, uid, programId, orgUnit, stage),
], batchActionTypes.UPDATE_FIELD_NEW_ENROLLMENT_ACTION_BATCH);
};

Expand All @@ -80,12 +121,13 @@ export const asyncUpdateSuccessBatch = (
itemId: string,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) => {
const uid = uuid();

return batchActions([
innerAction,
startRunRulesPostUpdateField(dataEntryId, itemId, uid),
startRunRulesOnUpdateForNewEnrollment({ ...innerAction.payload, dataEntryId, itemId }, uid, programId, orgUnit),
startRunRulesPostUpdateField(dataEntryId, itemId, uid, stage),
startRunRulesOnUpdateForNewEnrollment({ ...innerAction.payload, dataEntryId, itemId }, uid, programId, orgUnit, stage),
], batchActionTypes.UPDATE_FIELD_NEW_ENROLLMENT_ACTION_BATCH);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @flow
import type { OrgUnit } from '@dhis2/rules-engine-javascript';
import { actionCreator, actionPayloadAppender } from '../../../../actions/actions.utils';
import { ProgramStage } from '../../../../metaData';

export const actionTypes = {
START_RUN_RULES_ON_UPDATE: 'StartRunRulesOnUpdateForNewEnrollment',
Expand All @@ -11,9 +12,10 @@ export const startRunRulesOnUpdateForNewEnrollment = (
uid: string,
programId: string,
orgUnit: OrgUnit,
stage?: ProgramStage,
) =>
actionCreator(actionTypes.START_RUN_RULES_ON_UPDATE)(
{ innerPayload: payload, uid, programId, orgUnit });
{ innerPayload: payload, uid, programId, orgUnit, stage });

export const startAsyncUpdateFieldForNewEnrollment = (
innerAction: ReduxAction<any, any>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
import { batchActions } from 'redux-batched-actions';
import type { OrgUnit } from '@dhis2/rules-engine-javascript';
import { getApplicableRuleEffectsForTrackerProgram, updateRulesEffects } from '../../../../rules';
import type { TrackerProgram } from '../../../../metaData';
import type { ProgramStage, TrackerProgram } from '../../../../metaData';
import { getDataEntryKey } from '../../../DataEntry/common/getDataEntryKey';
import { loadNewDataEntry } from '../../../DataEntry/actions/dataEntryLoadNew.actions';
import { openDataEntryForNewEnrollment } from './open.actions';
import { getEnrollmentDateValidatorContainer, getIncidentDateValidatorContainer } from '../fieldValidators';
import { getEnrollmentDateValidatorContainer, getIncidentDateValidatorContainer, getReportDateValidatorContainers } from '../fieldValidators';
import { convertGeometryOut } from '../../converters';
import { convertDateObjectToDateFormatString } from '../../../../utils/converters/date';
import { addFormData } from '../../../D2Form/actions/form.actions';
Expand Down Expand Up @@ -47,6 +47,7 @@ export const openDataEntryForNewEnrollmentBatchAsync = async ({
extraDataEntryProps = [],
formValues,
clientValues,
firstStage,
}: {
program: TrackerProgram,
orgUnit: OrgUnit,
Expand All @@ -55,24 +56,41 @@ export const openDataEntryForNewEnrollmentBatchAsync = async ({
extraDataEntryProps?: Array<Object>,
formValues: { [key: string]: any },
clientValues: { [key: string]: any },
firstStage?: ?ProgramStage,
}) => {
const formId = getDataEntryKey(dataEntryId, itemId);
const addFormDataActions = addFormData(`${dataEntryId}-${itemId}`, formValues);
let effects;
const programStageDataEntryProps = [];
if (firstStage) {
effects = getApplicableRuleEffectsForTrackerProgram({
program,
orgUnit,
stage: firstStage,
attributeValues: clientValues,
});
programStageDataEntryProps.push({
id: 'stageOccurredAt',
type: 'DATE',
// $FlowFixMe[incompatible-call] automated comment
validatorContainers: getReportDateValidatorContainers(),
});
} else {
effects = getApplicableRuleEffectsForTrackerProgram({
program,
orgUnit,
attributeValues: clientValues,
});
}

const dataEntryActions =
loadNewDataEntry(
dataEntryId,
itemId,
[...dataEntryPropsToInclude, ...extraDataEntryProps],
[...dataEntryPropsToInclude, ...extraDataEntryProps, ...programStageDataEntryProps],
{ enrolledAt: convertDateObjectToDateFormatString(new Date()) },
);

const addFormDataActions = addFormData(`${dataEntryId}-${itemId}`, formValues);
const effects = getApplicableRuleEffectsForTrackerProgram({
program,
orgUnit,
attributeValues: clientValues,
});

return batchActions([
openDataEntryForNewEnrollment(
dataEntryId,
Expand Down
Loading