@Yousuf I have created the Python code for you…
I hope that it works without any problem…
import datetime
import frappe
from frappe import _, _dict
from frappe.utils import cint, flt, getdate
from frappe.model.meta import get_field_precision
from pypika.terms import Criterion
def get_employee_salary(employee, company, from_date, to_date):
try:
data = frappe.get_all(
"Salary Structure Assignment",
fields=["salary_structure", "base"],
filters={
"employee": employee,
"company": company,
"from_date": ["in", [from_date, to_date]]
},
limit_page_length=1,
)
data = data.pop(0) if data and isinstance(data, list) else None
if not data:
frappe.throw(_("Unable to find the employee in Salary Structure Assignment doctype."))
details = frappe.get_all(
"Salary Detail",
fields=[
"parentfield", "salary_component", "default_amount", "abbr", "amount",
"additional_salary", "condition", "formula", "amount_based_on_formula"
],
filters={
"parent": data["salary_structure"],
"parenttype": "Salary Structure",
"parentfield": ["in", ["earnings", "deductions"]]
}
)
if not details or not isinstance(details, list):
frappe.throw(_("Unable to get the salary details of the employee's Salary Structure entry."))
additional_salary = [row["additional_salary"] for row in details if row["additional_salary"]]
additionals = get_additional_salary(additional_salary, employee, company, from_date, to_date)
whitelisted_globals = {
"int": int,
"float": float,
"long": int,
"round": round,
"date": datetime.date,
"getdate": getdate,
}
local_data = {"base": data["base"]}
earnings = 0
deductions = 0
for row in details:
total = None
if row["additional_salary"]:
if row["additional_salary"] in additionals:
additional = additionals.get(row["additional_salary"])
if row["salary_component"] == additional["salary_component"]:
if additional["amount"] and cint(additional["overwrite_salary_structure_amount"]):
total = additional["amount"]
if not total:
total = eval_condition_and_formula(_dict(row), local_data, whitelisted_globals)
if not total:
continue
total = flt(total)
if row["abbr"]:
local_data[row["abbr"]] = total
if row["parentfield"] == "earnings":
earnings += total
else:
deductions += total
return flt(earnings - deductions)
except Exception as exc:
frappe.throw(_("Unable to calculate the employee's total salary."))
def get_additional_salary(names, employee, company, from_date, to_date):
doc = frappe.qb.DocType("Additional Salary")
data = (
frappe.qb.from_(doc)
.select(
doc.name,
doc.salary_component,
doc.amount,
doc.overwrite_salary_structure_amount
)
.where(doc.name.isin(names))
.where(doc.employee == employee)
.where(doc.company == company)
.where(
Criterion.any(
Criterion.all(
doc.is_recurring == 0,
doc.payroll_date.between(from_date, to_date)
),
Criterion.all(
doc.is_recurring == 1,
doc.from_date.gte(from_date),
doc.to_date.lte(to_date)
)
)
)
).run(as_dict=True)
if not data or not isinstance(data, list):
return {}
return {row["name"]:row for row in data}
def eval_condition_and_formula(row, data, whitelisted_globals):
try:
condition = row.condition.strip().replace("\n", " ") if row.condition else None
if condition:
if not frappe.safe_eval(condition, whitelisted_globals, data):
return None
amount = row.amount
if row.amount_based_on_formula:
formula = row.formula.strip().replace("\n", " ") if row.formula else None
if formula:
amount = flt(frappe.safe_eval(formula, whitelisted_globals, data), get_precision("amount"))
return amount
except NameError as err:
frappe.throw(
_("{0} <br> This error can be due to missing or deleted field.").format(err),
title=_("Name error"),
)
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
except Exception as e:
frappe.throw(_("Error in formula or condition: {0}").format(e))
raise
def get_precision(fieldname):
df = frappe.get_meta("Salary Detail").get_field(fieldname)
if df.fieldtype in ("Currency", "Float", "Percent"):
return get_field_precision(df)
return None