fix(payroll): resolve every salary slip field in component formulas - #5146
fix(payroll): resolve every salary slip field in component formulas#5146logarithmone1128 wants to merge 2 commits into
Conversation
Confidence Score: 5/5The PR appears safe to merge. The previously reported Long Int failure is fixed, and no blocking failure remains. Reviews (3): Last reviewed commit: "fix(payroll): classify slip fieldtypes w..." | Re-trigger Greptile |
|
Update on the verification caveat in the description: the two new tests have now been run on a real bench, so that gap is closed. Environment: With the change — the whole module passes: Reverting only So the tests do fail against the current behaviour rather than passing either way, and Note the CI, Linters, Patch and Documentation Required workflows on this PR are all sitting at |
A salary component formula may reference any Salary Slip field, but the evaluation context seeded only 21 hard-coded totals. Any other slip field raised "name is not defined" with a hint pointing at a missing or deleted field, even though the field exists. This surfaced when component evaluation moved to a pre-pass on the Salary Structure Assignment, which runs before a slip exists: the pre-pass had to hand-seed seven more period fields for the same reason, leaving two incomplete lists and no coverage for custom fields or for standard fields such as base_gross_pay. Derive the defaults from the Salary Slip meta instead. The result is a superset of the previous list, so every field that resolved before still resolves to the same 0, and precedence is unchanged: assignment fields, employee fields, the pre-pass period values, and the slip's own values all still override these defaults.
The local numeric set omitted Long Int, so a custom Long Int field was seeded as a string and broke arithmetic formulas -- the same failure mode as the hard-coded fieldname list this fixes. Use frappe.model.numeric_fieldtypes and no_value_fields instead of maintaining either set here. The no-value set also gains Attachment Gallery, which is present upstream on develop but was missing locally.
51a82ff to
c173ff9
Compare
Fixes #5057. Also reported as #5141.
A salary component formula may reference any Salary Slip field, but the evaluation
context seeded only 21 hard-coded totals. Referencing any other slip field fails at
salary slip creation with:
The hint is misleading — the field exists, and so does its column.
Details
SALARY_SLIP_EVAL_DEFAULTSinhrms/payroll/utils.pylisted 21 fieldnames, alltotals. It became load-bearing when component evaluation moved to a pre-pass on the
Salary Structure Assignment, which runs before any slip exists — so the pre-pass had
to hand-seed seven more fields (
start_date,end_date,payment_days,total_working_days,leave_without_pay,absent_days,unmarked_days) forexactly this reason. Its comment named the hazard but only for the fields it needed.
That leaves two incomplete lists and 34 of the doctype's own value-bearing fields
uncovered. It is not only custom fields:
base_gross_pay,exchange_rate,base_net_payand the loan fields this app installs itself(
total_principal_amount, viaget_salary_slip_loan_fields) all raise the sameNameError today.
The intended contract is already written down in this repo, in
test_get_evaluated_components_resolves_salary_slip_fields:A literal list cannot mirror a field set that includes custom fields.
Change
Replace the literal with
get_salary_slip_eval_defaults(), which derives thedefaults from
frappe.get_meta("Salary Slip"):0for numeric fieldtypes,""otherwise, skipping layout and table fieldtypes.
frappe.get_metais already cachedand invalidated on custom field changes, so no separate cache is introduced.
The SSA pre-pass keeps seeding the period values — those are needed for correct
full-cycle proration, not just to avoid a NameError — and its comment is updated to
say so.
Compatibility
The derived map is a strict superset of the previous literal: all 21 fieldnames are
real Salary Slip fields of type Currency or Float, so each still defaults to
0.Precedence in
get_component_eval_contextis untouched — assignment fields, thenemployee fields, override these defaults, as do the pre-pass period values and the
slip's own
as_dict()overlay. So a formula that resolved before resolves to thesame value; only formulas that previously raised now evaluate.
Verification
test_get_evaluated_components_resolves_slip_fields_outside_hardcoded_setcovers a slip field outside both lists (
base_gross_pay), which is the case thatfails on
develop.test_salary_slip_eval_defaults_cover_every_slip_fieldasserts every numericslip field is seeded and that table fieldtypes are excluded, so the coverage
cannot silently regress to a literal list again.
previous entries preserved as
0; layout and table fieldtypes excluded; thereported
custom_worked_hours * 100evaluates; and assignment, employee, andexplicit period values still win over the defaults.
ruff checkandruff format --checkclean on all three files.I could not run the integration suite locally (no bench), so the two new tests have
not been executed — happy to adjust if CI disagrees.
One note: #5105 also edits
get_component_eval_context, one line below this change.It touches the employee lookup rather than the defaults, so there is no overlap in
intent, but whichever lands second will need a trivial rebase.