fix: error messages while evaluating formulas and handle line boundaries (#35989)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
This commit is contained in:
@@ -46,6 +46,7 @@ from erpnext.payroll.doctype.payroll_period.payroll_period import (
|
||||
get_payroll_period,
|
||||
get_period_factor,
|
||||
)
|
||||
from erpnext.payroll.utils import prepare_error_msg, sanitize_expression
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
|
||||
@@ -726,32 +727,53 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
return data, default_data
|
||||
|
||||
def eval_condition_and_formula(self, d, data):
|
||||
def eval_condition_and_formula(self, struct_row, data):
|
||||
try:
|
||||
condition = d.condition.strip().replace("\n", " ") if d.condition else None
|
||||
condition = sanitize_expression(struct_row.condition)
|
||||
if condition:
|
||||
if not frappe.safe_eval(condition, self.whitelisted_globals, data):
|
||||
return None
|
||||
amount = d.amount
|
||||
if d.amount_based_on_formula:
|
||||
formula = d.formula.strip().replace("\n", " ") if d.formula else None
|
||||
amount = struct_row.amount
|
||||
if struct_row.amount_based_on_formula:
|
||||
formula = sanitize_expression(struct_row.formula)
|
||||
if formula:
|
||||
amount = flt(frappe.safe_eval(formula, self.whitelisted_globals, data), d.precision("amount"))
|
||||
amount = flt(
|
||||
frappe.safe_eval(formula, self.whitelisted_globals, data), struct_row.precision("amount")
|
||||
)
|
||||
if amount:
|
||||
data[d.abbr] = amount
|
||||
data[struct_row.abbr] = 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 NameError as ne:
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=ne,
|
||||
expression=formula or condition,
|
||||
description=_("This error can be due to missing or deleted field."),
|
||||
)
|
||||
except SyntaxError as err:
|
||||
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
||||
|
||||
frappe.throw(message, title=_("Name error"))
|
||||
|
||||
except SyntaxError as se:
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=se,
|
||||
expression=formula or condition,
|
||||
description=_("Please check the syntax of your formula."),
|
||||
)
|
||||
|
||||
frappe.throw(message, title=_("Syntax error"))
|
||||
|
||||
except Exception as e:
|
||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||
raise
|
||||
message = prepare_error_msg(
|
||||
row=struct_row,
|
||||
error=e,
|
||||
expression=formula or condition,
|
||||
description=_("This error can be due to invalid formula or condition."),
|
||||
)
|
||||
|
||||
frappe.throw(message, title=_("Error in formula or condition"))
|
||||
|
||||
def add_employee_benefits(self, payroll_period):
|
||||
for struct_row in self._salary_structure_doc.get("earnings"):
|
||||
|
||||
66
erpnext/payroll/utils.py
Normal file
66
erpnext/payroll/utils.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from typing import Optional
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_link_to_form
|
||||
|
||||
|
||||
def sanitize_expression(string: Optional[str] = None) -> Optional[str]:
|
||||
"""
|
||||
Sanitizes an expression string by removing leading/trailing spaces, newlines, and line boundaries.
|
||||
|
||||
Args:
|
||||
string (Optional[str]): The input string to be sanitized (default: None).
|
||||
|
||||
Returns:
|
||||
Optional[str]: The sanitized string or None if the input string is empty or None.
|
||||
|
||||
Example:
|
||||
expression = "\r\n gross_pay > 10000\n "
|
||||
sanitized_expr = sanitize_expression(expression)
|
||||
"""
|
||||
if not string:
|
||||
return None
|
||||
|
||||
parts = string.strip().splitlines()
|
||||
string = " ".join(parts)
|
||||
|
||||
return string
|
||||
|
||||
|
||||
def prepare_error_msg(*, row: dict, error: str, expression: str, description: str) -> str:
|
||||
"""
|
||||
Prepares an error message string with formatted information about the error.
|
||||
|
||||
Args:
|
||||
row (dict): A dictionary representing the row data.
|
||||
error (str): The error message.
|
||||
expression (str): The expression that caused the error.
|
||||
description (str): Additional description or hint for the error (optional).
|
||||
|
||||
Returns:
|
||||
str: The formatted error message string.
|
||||
|
||||
Example:
|
||||
row = {
|
||||
"parenttype": "Salary Structure",
|
||||
"parent": "Salary Structure-00001",
|
||||
"parentfield": "earnings",
|
||||
"idx": 1
|
||||
}
|
||||
error = "SyntaxError: invalid syntax"
|
||||
expression = " 200 if (gross_pay>10000 and month!= 'Feb')) else 0 "
|
||||
description = "Check the syntax of the expression."
|
||||
error_msg = prepare_error_msg(row=row, error=error, expression=expression, description=description)
|
||||
"""
|
||||
msg = _("Error in {0} while evaluating the {1} {2} at row {3}").format(
|
||||
row.parentfield.title(), row.parenttype, get_link_to_form(row.parenttype, row.parent), row.idx
|
||||
)
|
||||
msg += "<br><br>{0}: {1}<br><br>{2}: {3}".format(
|
||||
frappe.bold(_("Expression:")), expression, frappe.bold(_("Error:")), error
|
||||
)
|
||||
|
||||
if description:
|
||||
msg += "<br><br>{0}: {1}".format(frappe.bold(_("Hint:")), description)
|
||||
|
||||
return msg
|
||||
Reference in New Issue
Block a user