Skip to content

feat: patient registration with expiry period#1043

Open
akurungadam wants to merge 6 commits into
earthians:developfrom
akurungadam:register
Open

feat: patient registration with expiry period#1043
akurungadam wants to merge 6 commits into
earthians:developfrom
akurungadam:register

Conversation

@akurungadam

@akurungadam akurungadam commented Jun 22, 2026

Copy link
Copy Markdown
Member

@akurungadam akurungadam marked this pull request as ready for review June 22, 2026 11:58
@akurungadam akurungadam requested a review from Sajinsr as a code owner June 22, 2026 11:58
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Walkthrough

A new Patient Registration DocType is introduced to track per-patient registration validity. Healthcare Settings gains a registration_validity (days) field. On patient creation without a fee, or on Sales Invoice submit/cancel, Patient Registration records are created or cancelled accordingly. A renew_registration server method and updated client-side buttons handle manual reactivation. A daily scheduler job (expire_registrations) marks lapsed registrations as Expired and disables patients with no remaining active registration. A backfill patch creates Patient Registration records for all existing active patients missing one.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: patient registration with expiry period' accurately summarizes the main feature being introduced—a new patient registration system with configurable expiry periods.
Description check ✅ Passed The description is related to the changeset, providing context about the feature through documentation screenshots and referencing the Healthcare Settings configuration for registration validity.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
healthcare/healthcare/doctype/patient/patient.js (1)

25-34: 🧹 Nitpick | 🔵 Trivial

Consider coercing checkbox defaults for defensive programming.

frappe.defaults.get_default('collect_registration_fee') may return string values like "0" depending on the Frappe version and serialization behavior. String "0" is truthy in JavaScript, which could cause the wrong button to display. Use cint() to safely normalize the value before branching.

Note: Similar patterns exist elsewhere in the codebase (sample_collection.js:56, lab_test.js:56) without coercion, suggesting this may be version-dependent. However, defensive coercion here is a safe improvement.

Suggested coercion
 		if (frm.doc.status == 'Disabled') {
-			if (frappe.defaults.get_default('collect_registration_fee')) {
+			if (cint(frappe.defaults.get_default('collect_registration_fee'))) {
 				frm.add_custom_button(__('Invoice Patient Registration'), function () {
 					invoice_registration(frm);
 				});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@healthcare/healthcare/doctype/patient/patient.js` around lines 25 - 34, The
condition checking frappe.defaults.get_default('collect_registration_fee') is
vulnerable to string-to-boolean coercion issues, where the string "0" would
evaluate as truthy and cause the wrong button to display. Wrap the
frappe.defaults.get_default('collect_registration_fee') call with cint() to
safely convert the value to a proper integer before the conditional evaluation,
ensuring reliable branching logic regardless of the Frappe version's
serialization behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@healthcare/healthcare/doctype/healthcare_settings/healthcare_settings.json`:
- Around line 108-116: The registration_validity field in
healthcare_settings.json is gated behind the collect_registration_fee setting
using the depends_on condition with "eval:doc.collect_registration_fee == 1".
Remove the depends_on line entirely from the registration_validity field
definition so that administrators can configure registration expiry
independently of whether fee collection is enabled, allowing registration
validity to be configured for all registration flows regardless of fee
collection status.

In `@healthcare/healthcare/doctype/patient_registration/patient_registration.py`:
- Around line 64-69: The function disable_patient_without_active_registration is
checking for the existence of any Active registration status without validating
whether that registration is currently valid (checking if valid_till date hasn't
passed). Modify the frappe.db.exists query to include an additional condition
that verifies the valid_till field is greater than or equal to today's date,
ensuring only registrations with valid dates prevent the patient from being
disabled. This prevents stale Active registrations from keeping a patient
enabled when their registration has actually expired.

In
`@healthcare/healthcare/doctype/patient_registration/test_patient_registration.py`:
- Around line 19-20: The get_patient() method directly indexes [0] on the
frappe.get_list result without checking if the list is empty, which causes an
IndexError when no Patient records exist in the test database. Add a guard check
before indexing into the result list to either return None, raise a meaningful
error, or use a safer access pattern (like next(iter(...), None)) to handle the
empty case gracefully.

In `@healthcare/healthcare/doctype/patient/patient.py`:
- Around line 25-27: The `renew_registration` method in the Patient doctype
needs server-side validation guards to prevent bypassing fee/no-fee split logic
and to enforce renewal eligibility. Add checks within the `renew_registration`
whitelisted method to: verify that when `collect_registration_fee` is enabled,
the appropriate fee collection flow is enforced (preventing free registrations),
and validate that a disabled patient does not get re-enabled if they already
have a current active registration. These guards should run before any patient
reactivation logic to ensure renewal eligibility is properly enforced at the
server level.

---

Nitpick comments:
In `@healthcare/healthcare/doctype/patient/patient.js`:
- Around line 25-34: The condition checking
frappe.defaults.get_default('collect_registration_fee') is vulnerable to
string-to-boolean coercion issues, where the string "0" would evaluate as truthy
and cause the wrong button to display. Wrap the
frappe.defaults.get_default('collect_registration_fee') call with cint() to
safely convert the value to a proper integer before the conditional evaluation,
ensuring reliable branching logic regardless of the Frappe version's
serialization behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a992b9fa-e440-46a3-b113-f511c423ca19

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca3a92 and ee5cbf2.

📒 Files selected for processing (12)
  • healthcare/healthcare/doctype/healthcare_settings/healthcare_settings.json
  • healthcare/healthcare/doctype/patient/patient.js
  • healthcare/healthcare/doctype/patient/patient.py
  • healthcare/healthcare/doctype/patient_registration/__init__.py
  • healthcare/healthcare/doctype/patient_registration/patient_registration.js
  • healthcare/healthcare/doctype/patient_registration/patient_registration.json
  • healthcare/healthcare/doctype/patient_registration/patient_registration.py
  • healthcare/healthcare/doctype/patient_registration/test_patient_registration.py
  • healthcare/healthcare/utils.py
  • healthcare/hooks.py
  • healthcare/patches.txt
  • healthcare/patches/v16_0/create_registration_for_existing_patients.py

Comment on lines +25 to +27
from healthcare.healthcare.doctype.patient_registration.patient_registration import (
create_patient_registration,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce renewal eligibility in the whitelisted method.

renew_registration is callable directly, so the client-only fee/no-fee split can be bypassed. When collect_registration_fee is enabled, this can create a free registration and reactivate the patient; also avoid re-enabling a disabled patient that already has a current active registration.

Suggested server-side guard
 from healthcare.healthcare.doctype.patient_registration.patient_registration import (
 	create_patient_registration,
+	has_active_patient_registration,
 )
 	`@frappe.whitelist`()
 	def renew_registration(self):
 		"""Create a fresh registration and re-enable the patient (when no fee is collected)."""
-		if not frappe.db.exists("Patient Registration", {"patient": self.name, "status": "Active"}):
-			create_patient_registration(self.name)
+		if frappe.db.get_single_value("Healthcare Settings", "collect_registration_fee"):
+			frappe.throw(_("Invoice the patient registration fee before renewing this registration."))
+		if has_active_patient_registration(self.name):
+			frappe.throw(_("Patient already has an active registration."))
+		create_patient_registration(self.name)
 		self.db_set("status", "Active")

Also applies to: 186-191

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@healthcare/healthcare/doctype/patient/patient.py` around lines 25 - 27, The
`renew_registration` method in the Patient doctype needs server-side validation
guards to prevent bypassing fee/no-fee split logic and to enforce renewal
eligibility. Add checks within the `renew_registration` whitelisted method to:
verify that when `collect_registration_fee` is enabled, the appropriate fee
collection flow is enforced (preventing free registrations), and validate that a
disabled patient does not get re-enabled if they already have a current active
registration. These guards should run before any patient reactivation logic to
ensure renewal eligibility is properly enforced at the server level.

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.

1 participant