Skip to content

Commit

Permalink
feat: allow adding formula to calculate final score in appraisal cycle
Browse files Browse the repository at this point in the history
(cherry picked from commit 1712990)

# Conflicts:
#	hrms/hr/doctype/appraisal_cycle/appraisal_cycle.json
  • Loading branch information
vinyselopal authored and mergify[bot] committed May 29, 2024
1 parent 515a44b commit 76541b6
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 5 deletions.
32 changes: 29 additions & 3 deletions hrms/hr/doctype/appraisal/appraisal.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from hrms.hr.doctype.appraisal_cycle.appraisal_cycle import validate_active_appraisal_cycle
from hrms.hr.utils import validate_active_employee
from hrms.payroll.utils import sanitize_expression


class Appraisal(Document):
Expand Down Expand Up @@ -179,9 +180,34 @@ def calculate_avg_feedback_score(self, update=False):
self.db_update()

def calculate_final_score(self):
final_score = (flt(self.total_score) + flt(self.avg_feedback_score) + flt(self.self_score)) / 3

self.final_score = flt(final_score, self.precision("final_score"))
if self.appraisal_cycle:
final_score = 0
appraisal_cycle_doc = frappe.get_doc("Appraisal Cycle", self.appraisal_cycle)
employee_doc = frappe.get_doc("Employee", self.employee)

formula = appraisal_cycle_doc.final_score_formula
based_on_formula = appraisal_cycle_doc.calculate_final_score_based_on_formula

if not based_on_formula == 1:
final_score = (
flt(self.total_score) + flt(self.avg_feedback_score) + flt(self.self_score)
) / 3
else:
sanitized_formula = sanitize_expression(formula)

data = {
"goal_score": flt(self.total_score),
"average_feedback_score": flt(self.avg_feedback_score),
"self_appraisal_score": flt(self.self_score),
}

data.update(appraisal_cycle_doc.as_dict())
data.update(employee_doc.as_dict())
data.update(self.as_dict())

final_score = frappe.safe_eval(sanitized_formula, data)

self.final_score = flt(final_score, self.precision("final_score"))

@frappe.whitelist()
def add_feedback(self, feedback, feedback_ratings):
Expand Down
20 changes: 18 additions & 2 deletions hrms/hr/doctype/appraisal/test_appraisal.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,24 @@ def test_manual_kra_rating(self):
self.assertEqual(appraisal.final_score, 1.2)

def test_final_score(self):
cycle = create_appraisal_cycle(designation="Engineer", kra_evaluation_method="Manual Rating")
cycle = create_appraisal_cycle(
designation="Engineer", kra_evaluation_method="Manual Rating", based_on_formula=0
)
cycle.create_appraisals()
appraisal = self.setup_appraisal_cycle(cycle)

self.assertEqual(appraisal.final_score, 3.767)

def test_final_score_using_formula(self):
cycle = create_appraisal_cycle(
designation="Engineer", kra_evaluation_method="Manual Rating", based_on_formula=1
)
cycle.create_appraisals()
appraisal = self.setup_appraisal_cycle(cycle)

self.assertEqual(appraisal.final_score, 3.8)

def setup_appraisal_cycle(self, cycle):
appraisal = frappe.db.exists("Appraisal", {"appraisal_cycle": cycle.name, "employee": self.employee1})
appraisal = frappe.get_doc("Appraisal", appraisal)

Expand Down Expand Up @@ -97,7 +112,8 @@ def test_final_score(self):
feedback.submit()

appraisal.reload()
self.assertEqual(appraisal.final_score, 3.767)

return appraisal

def test_goal_score(self):
"""
Expand Down
38 changes: 38 additions & 0 deletions hrms/hr/doctype/appraisal_cycle/appraisal_cycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,44 @@

frappe.ui.form.on("Appraisal Cycle", {
refresh(frm) {
async function set_autocompletions_for_final_score_formula(frm) {
const autocompletions = [
{
value: "goal_score",
score: 8,
meta: "Goal field",
},
{
value: "average_feedback_score",
score: 8,
meta: "Appraisal field",
},
{
value: "self_appraisal_score",
score: 8,
meta: "Appraisal field",
},
];

const doctypes = ["Employee", "Appraisal Cycle"];

await Promise.all(
doctypes.map((doctype) =>
frappe.model.with_doctype(doctype, () => {
autocompletions.push(
...frappe.get_meta(doctype).fields.map((f) => ({
value: f.fieldname,
score: 8,
meta: __("{0} Field", [doctype]),
})),
);
}),
),
);
frm.set_df_property("final_score_formula", "autocompletions", autocompletions);
}

set_autocompletions_for_final_score_formula(frm);
frm.set_query("department", () => {
return {
filters: {
Expand Down
25 changes: 25 additions & 0 deletions hrms/hr/doctype/appraisal_cycle/appraisal_cycle.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"section_break_4",
"description",
"settings_section",
"calculate_final_score_based_on_formula",
"final_score_formula",
"column_break_wpgg",
"kra_evaluation_method",
"applicable_for_tab",
"filters_section",
Expand Down Expand Up @@ -154,6 +157,24 @@
"label": "Status",
"options": "Not Started\nIn Progress\nCompleted",
"read_only": 1
},
{
"depends_on": "eval:doc.calculate_final_score_based_on_formula === 1;",
"fieldname": "final_score_formula",
"fieldtype": "Code",
"label": "Final Score Formula",
"max_height": "5rem",
"options": "PythonExpression"
},
{
"default": "0",
"fieldname": "calculate_final_score_based_on_formula",
"fieldtype": "Check",
"label": "Calculate final score based on formula"
},
{
"fieldname": "column_break_wpgg",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
Expand All @@ -171,7 +192,11 @@
"link_fieldname": "appraisal_cycle"
}
],
<<<<<<< HEAD
"modified": "2023-03-29 12:28:36.247120",
=======
"modified": "2024-04-16 13:15:55.493958",
>>>>>>> 1712990de (feat: allow adding formula to calculate final score in appraisal cycle)
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Cycle",
Expand Down
8 changes: 8 additions & 0 deletions hrms/hr/doctype/appraisal_cycle/test_appraisal_cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,23 @@ def create_appraisal_cycle(**args):
if frappe.db.exists("Appraisal Cycle", name):
frappe.delete_doc("Appraisal Cycle", name, force=True)

based_on_formula = args.based_on_formula

appraisal_cycle = frappe.get_doc(
{
"doctype": "Appraisal Cycle",
"cycle_name": name,
"company": args.company or "_Test Appraisal",
"start_date": args.start_date or "2022-01-01",
"end_date": args.end_date or "2022-03-31",
"calculate_final_score_based_on_formula": based_on_formula,
}
)
if based_on_formula:
appraisal_cycle.final_score_formula = (
args.final_score_formula
or "goal_score * 0.2 + self_appraisal_score * 0.2 + average_feedback_score * 0.6"
)

if args.kra_evaluation_method:
appraisal_cycle.kra_evaluation_method = args.kra_evaluation_method
Expand Down
1 change: 1 addition & 0 deletions hrms/public/js/hrms.bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import "./templates/rating.html";
import "./utils";
import "./utils/payroll_utils";
import "./utils/leave_utils";
import "./utils/appraisal_utils";

0 comments on commit 76541b6

Please sign in to comment.