Skip to content

HTML-882: allow creating appointment in edit mode in ScheduleAppoinme… - #345

Merged
chibongho merged 2 commits into
masterfrom
HTML-882
Aug 4, 2026
Merged

chibongho merged 2 commits into
masterfrom
HTML-882

Conversation

@chibongho

Copy link
Copy Markdown
Contributor

…nt tag

This PR modifies that existing <scheduleAppointment>, which only allows for scheduling a new appointment in ENTER mode. With this change, scheduling a new appointment in EDIT mode is allowed, provided that no appointment has been scheduled in prior ENTER / EDIT mode submissions.

We retrain the existing behavior that once an appointment has been created by the form, the appointment cannot be modified in subsequent EDIT mode. (The user will need to go to the appointment app to do so.)

@chibongho
chibongho requested review from cioan, dkayiwa, mogoodrich and mseaton and removed request for mseaton July 27, 2026 17:47

@mogoodrich mogoodrich left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @chibongho !

Comment on lines +137 to +140
// EDIT mode with no previously-scheduled appointment: let the user schedule one now.
if (context.getMode() == Mode.EDIT && uuidObs == null) {
allowScheduling = true;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes an appointment mandatory in EDIT mode rather than merely possible. allowScheduling also gates validateSubmission, so all seven required fields now apply to an edit.

I ran the same api-tests probe against both branches. On master, editing an encounter that carries no appointment-uuid obs submits cleanly. On this branch the identical edit comes back with seven errors (Location, Service, Appointment type, Appointment date, Appointment time, Duration, Provider) and never reaches applyActions.

Merged as is, that means adding <scheduleAppointment> to a form that already has encounters makes every one of those encounters unsaveable in EDIT: someone correcting a diagnosis can't save until they also book a future appointment. optional="true" is milder but still caught, since generateHtml emits value="yes" checked and a browser submits that by default, so the user has to click "No" on every edit even though they already declined at entry time.

My suggestion is to make scheduling opt-in whenever the mode is EDIT: turn the yes/no choice on regardless of the optional attribute, check "no" rather than "yes" in that mode, and start the fields wrapper hidden to match. Validation and handleSubmission then only engage once the user asks for an appointment. Skipping validation in EDIT when every appointment field is blank would also work, but it gives the user no visible signal, so I'd go with the explicit choice.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts @chibongho , this does need to be fixed if it does work as Daniel says

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be fixed. I'll make it so in EDIT mode, if no appointment has been scheduled yet, it defaults to "Don't schedule appointment" instead, regardless of the optional attribute.

else {
// VIEW/EDIT: claim the UUID obs so HFE doesn't warn about an unmatched obs,
// then fetch the appointment for display in generateHtml().
Obs uuidObs = StringUtils.isNotBlank(tagControlId)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With a controlId set, this lookup only matches obs whose formFieldPath carries that control id, so an appointment recorded while the tag had no controlId isn't found. Under the new rule, uuidObs == null now means "offer another one".

I reproduced it in api-tests: seed encounter 101 with an appointment-uuid obs whose formFieldPath is null (exactly what handleSubmission writes when no controlId is configured), then open it in EDIT with controlId="appt1" on the tag. Master renders the "No appointment scheduled" message, wrong but harmless. This branch renders the full scheduling form, and submitting it leaves the encounter with two appointment-uuid obs and the patient with two live appointments. The encounter then stops rendering at all, because two obs sharing a control id make getObsFromExistingObs throw IllegalStateException: Multiple obs are matching the control id 'appt1' in both VIEW and EDIT (that last part is framework behaviour, identical on master).

Adding a controlId to an existing tag is a routine form edit, so I'd rather this didn't hinge on the two lookups agreeing. Before concluding that nothing was scheduled, check whether the encounter has any obs for uuidConcept at all (the existingObs entry, read ahead of the remove) instead of trusting the controlId-scoped match. That also covers the mirror case, where a controlId gets dropped from a tag and the already-stamped formFieldPath makes removeExistingObs miss.

It takes a deliberate form change to trigger, so I don't think it has to hold up the merge if you'd rather split it out, but it's this PR that turns the mismatch from a cosmetic one into duplicate clinical data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this problem happens when we first record an appointment in a form with no controlId, then change the form so that the tag has a controlId, and then if we EDIT the form, it doesn't know that the prior appointment has already been created.

Given that we expect users to version bump the form if they make edits to it, and that this is also how controlId in the <obs> tag works (if we change the controlId of <obs>, prior obs recordings will not be matched properly onto the right field), I think this is fine? Let me know if you disagree.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with what @chibongho says above (the onus is on the implementer to upgrade the form version). Alternatively, since this is a new tag, we could just mandate that a controlId is used? (This is some that should be used for all data points, but it didn't exist back in the early days).

assertNull(obsToCreate.get(0).getFormFieldPath());
}

// --- EDIT mode: scheduling allowed only when no appointment exists yet ---

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two exercise handleSubmission, but the change also flips generateHtml and validateSubmission into scheduling mode in EDIT, and neither of those is covered. handleSubmission_editMode_existingAppointment_shouldBeNoOp also passes unchanged on master, where EDIT is already a no-op, so it reads as a guard rather than a check on the new behaviour.

ScheduleAppointmentTagTest in api-tests is where I'd put this. It already has doEditEncounter(), testEditFormHtml and setupEditRequest, and an EDIT-mode case there pins down what the PR is actually for: open an encounter with no appointment, fill the form, assert the appointment and its uuid obs land on the existing encounter. I ran exactly that against this branch and it passes, so it's a few lines of harness work rather than new debugging. It would also have caught the validation behaviour I raised on the element, which a mocked FormEntryContext can't reach because it never renders the form or runs a submission.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add EDIT mode tests in ScheduleAppointmentTagTest

@chibongho
chibongho requested a review from dkayiwa August 3, 2026 14:52
@chibongho
chibongho merged commit d7ece05 into master Aug 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants