fix: error messages while evaluating formulas and handle line boundaries (#35989)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
This commit is contained in:
Saurabh
2023-07-11 18:04:31 +05:30
committed by GitHub
parent e05bb103f3
commit 4af57a7318
2 changed files with 103 additions and 15 deletions

View File

@@ -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
View 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