diff --git a/healthcare/healthcare/doctype/observation/observation.py b/healthcare/healthcare/doctype/observation/observation.py
index a0caf3b0c4..e4bcb81e7b 100644
--- a/healthcare/healthcare/doctype/observation/observation.py
+++ b/healthcare/healthcare/doctype/observation/observation.py
@@ -8,7 +8,7 @@
from frappe import _
from frappe.model.document import Document
from frappe.model.workflow import get_workflow_name, get_workflow_state_field
-from frappe.utils import now_datetime
+from frappe.utils import flt, get_link_to_form, getdate, now_datetime, nowdate
from erpnext.setup.doctype.terms_and_conditions.terms_and_conditions import (
get_terms_and_conditions,
@@ -25,6 +25,12 @@ def validate(self):
def on_update(self):
set_diagnostic_report_status(self)
+ if (
+ self.parent_observation
+ and self.result_data
+ and self.permitted_data_type in ["Quantity", "Numeric"]
+ ):
+ set_calculated_result(self)
def before_insert(self):
set_observation_idx(self)
@@ -482,3 +488,116 @@ def set_diagnostic_report_status(doc):
frappe.db.set_value(
"Diagnostic Report", diagnostic_report.get("name"), set_value_dict, update_modified=False
)
+
+
+def set_calculated_result(doc):
+ if doc.parent_observation:
+ parent_template = frappe.db.get_value(
+ "Observation", doc.parent_observation, "observation_template"
+ )
+ parent_template_doc = frappe.get_cached_doc("Observation Template", parent_template)
+
+ data = frappe._dict()
+ patient_doc = frappe.get_cached_doc("Patient", doc.patient).as_dict()
+ settings = frappe.get_cached_doc("Healthcare Settings").as_dict()
+
+ data.update(doc.as_dict())
+ data.update(parent_template_doc.as_dict())
+ data.update(patient_doc)
+ data.update(settings)
+
+ for component in parent_template_doc.observation_component:
+ """
+ Data retrieval from observations has been moved into the loop
+ to accommodate component observations, which may contain formulas
+ utilizing results from previous iterations.
+
+ """
+ if component.based_on_formula and component.formula:
+ obs_data = get_data(doc, parent_template_doc)
+ else:
+ continue
+
+ if obs_data and len(obs_data) > 0:
+ data.update(obs_data)
+ result = eval_condition_and_formula(component, data)
+ if not result:
+ continue
+
+ result_observation_name, result_data = frappe.db.get_value(
+ "Observation",
+ {
+ "parent_observation": doc.parent_observation,
+ "observation_template": component.get("observation_template"),
+ },
+ ["name", "result_data"],
+ )
+ if result_observation_name and result_data != str(result):
+ frappe.db.set_value(
+ "Observation",
+ result_observation_name,
+ "result_data",
+ str(result),
+ )
+
+
+def get_data(doc, parent_template_doc):
+ data = frappe._dict()
+ observation_details = frappe.get_all(
+ "Observation",
+ {"parent_observation": doc.parent_observation},
+ ["observation_template", "result_data"],
+ )
+
+ # to get all result_data to map against abbs of all table rows
+ for component in parent_template_doc.observation_component:
+ result = [
+ d["result_data"]
+ for d in observation_details
+ if (d["observation_template"] == component.get("observation_template") and d["result_data"])
+ ]
+ data[component.get("abbr")] = flt(result[0]) if (result and len(result) > 0 and result[0]) else 0
+ return data
+
+
+def eval_condition_and_formula(d, data):
+ try:
+ if d.get("condition"):
+ cond = d.get("condition")
+ parts = cond.strip().splitlines()
+ condition = " ".join(parts)
+ if condition:
+ if not frappe.safe_eval(condition, data):
+ return None
+
+ if d.based_on_formula:
+ amount = None
+ formula = d.formula.strip().replace("\n", " ") if d.formula else None
+ operands = re.split(r"\W+", formula)
+ abbrs = [operand for operand in operands if re.search(r"[a-zA-Z]", operand)]
+ if "age" in abbrs and data.get("dob"):
+ age = (
+ getdate(nowdate()).year
+ - data.get("dob").year
+ - (
+ (getdate(nowdate()).month, getdate(nowdate()).day)
+ < (data.get("dob").month, data.get("dob").day)
+ )
+ )
+ if age > 0:
+ data["age"] = age
+
+ # check the formula abbrs has result value
+ abbrs_present = all(abbr in data and data[abbr] != 0 for abbr in abbrs)
+ if formula and abbrs_present:
+ amount = flt(frappe.safe_eval(formula, {}, data))
+
+ return amount
+
+ except Exception as err:
+ description = _("This error can be due to invalid formula.")
+ message = _(
+ """Error while evaluating the {0} {1} at row {2}.
Error: {3}
+
Hint: {4}"""
+ ).format(d.parenttype, get_link_to_form(d.parenttype, d.parent), d.idx, err, description)
+ frappe.throw(message, title=_("Error in formula"))
diff --git a/healthcare/healthcare/doctype/observation_component/observation_component.json b/healthcare/healthcare/doctype/observation_component/observation_component.json
index cb7b498a85..823e4c2a06 100644
--- a/healthcare/healthcare/doctype/observation_component/observation_component.json
+++ b/healthcare/healthcare/doctype/observation_component/observation_component.json
@@ -8,9 +8,11 @@
"field_order": [
"observation_template",
"abbr",
+ "condition",
"column_break_seht",
"based_on_formula",
- "formula"
+ "formula",
+ "note"
],
"fields": [
{
@@ -30,7 +32,7 @@
"fieldname": "based_on_formula",
"fieldtype": "Check",
"in_list_view": 1,
- "label": "Based On Formula"
+ "label": "Based on Condition and Formula"
},
{
"depends_on": "based_on_formula",
@@ -47,12 +49,24 @@
"in_list_view": 1,
"label": "Abbr",
"read_only": 1
+ },
+ {
+ "depends_on": "based_on_formula",
+ "fieldname": "condition",
+ "fieldtype": "Code",
+ "label": "Condition"
+ },
+ {
+ "depends_on": "based_on_formula",
+ "fieldname": "note",
+ "fieldtype": "HTML",
+ "options": "
Notes:
\n\nage
Patient, Healthcare Settings, Observation, Observation Template