Applying the FLSA Salary-Basis Test for Exempt Classification
A payroll engine that stops at “does this salary clear the federal floor” will misclassify an employee who is docked half a day’s pay for leaving early, suspended for two days as discipline, or paid on an hourly-equivalent schedule dressed up as a weekly figure — because the white-collar exemptions require a predetermined amount paid on a true salary basis, not merely a salary-sized number. This page codes the classification gate itself: the sequence of tests — salary level, salary basis, and duties — a payroll system must evaluate, in that order, before a record is trusted with exempt status, and it is part of the FLSA Threshold Mapping topic within the Core Architecture & Compliance Mapping framework.
Problem Framing
Most engines get the salary-level test right and stop there, because it is the arithmetic-looking half of the exemption: annualize the pay rate, compare it to a floor, done. That comparison — governed by 29 CFR § 541.600 — is necessary but not sufficient, and treating it as sufficient produces two distinct classes of misclassification that a level-only gate cannot see:
- Salary-basis violations. 29 CFR § 541.602 requires the salary to be a predetermined amount “not subject to reduction because of variations in the quality or quantity of the work performed.” An employer that docks an exempt employee’s pay for a partial-day absence, or suspends them for two days without invoking a permissible category, has converted a salaried position into something paid by the hour in substance — and the regulation does not care that the annualized figure still clears the floor.
- The “actual practice” trap. A single improper deduction does not automatically strip exemption from the whole workforce, but a pattern of improper deductions does, under the actual-practice doctrine embedded in § 541.603. An engine that flags every deduction identically — one isolated, reimbursed error the same as a repeated payroll-wide policy — either overreacts on a harmless correction or, worse, misses the pattern because no single instance looks disqualifying in isolation.
The duties test compounds this: 29 CFR § 541.700 requires an evaluation of actual job responsibilities that no payroll field can determine on its own. A classification gate that treats “salary clears the floor” as equivalent to “exempt” silently promotes untested duties determinations into a compliance record. The correct architecture resolves salary level and salary basis deterministically in code, and treats the duties result as an external input the gate consumes but never infers — flagging it for review rather than guessing.
The weekly-to-annual comparison that anchors the salary-level test is a fixed multiplier, applied before any threshold comparison, exactly as in the parent registry’s annualization rule:
Prerequisites & Data Requirements
The gate consumes a normalized compensation record plus a deduction event log; it does not compute the salary basis from raw timesheets, and it does not infer duties from a job title.
| Field | Type | Precondition |
|---|---|---|
weekly_salary |
Decimal |
Predetermined weekly amount; already isolated from bonuses per Data Boundary Definitions. |
weekly_threshold |
Decimal |
Resolved by FLSA Threshold Mapping for the employee’s jurisdiction and effective date; never hardcoded here. |
deduction_events |
List[DeductionEvent] |
Each event carries a reason_code, amount, and is_full_day flag; empty list means no deductions this period. |
duties_test_result |
str enum |
One of PASS, FAIL, UNDETERMINED, supplied by HR/legal — never derived from payroll fields. |
has_communicated_policy |
bool |
Whether a written anti-improper-deduction policy with a complaint mechanism is on file, required for the § 541.603 safe harbor. |
deduction_reimbursed |
bool |
Whether an improper deduction was reimbursed before the safe-harbor evaluation ran. |
Two preconditions sit outside the function signature:
- The threshold is resolved, not chosen here. This module calls into the same effective-dated registry built in FLSA Threshold Mapping; it never embeds a dollar figure, because the federal floor and any higher state or municipal floor both change on statutory schedules.
- Duties are an input, not an output.
duties_test_resultmust arrive from a system of record for job-duties determinations. A gate that defaultsUNDETERMINEDtoPASSconverts a missing HR review into a fabricated exempt classification.
Step-by-Step Implementation
Step 1 — Model the record and the deduction reason taxonomy. Separate permissible deduction categories under § 541.602(b) from everything else, so the detector never has to special-case strings later.
from dataclasses import dataclass, field
from decimal import Decimal
from enum import Enum
from typing import List
import logging
logger = logging.getLogger("payroll.flsa.salary_basis")
WEEKS_PER_YEAR = Decimal("52")
class DeductionReason(str, Enum):
FULL_DAY_PERSONAL = "full_day_personal" # permissible: 541.602(b)(1)
FULL_DAY_SICKNESS_PLAN = "full_day_sickness_plan" # permissible: 541.602(b)(2)
JURY_WITNESS_MILITARY_OFFSET = "jury_witness_offset" # permissible: 541.602(b)(3)
SAFETY_RULE_PENALTY = "safety_rule_penalty" # permissible: 541.602(b)(4)
CONDUCT_SUSPENSION_FULL_DAY = "conduct_suspension_full_day" # permissible: 541.602(b)(5)
FIRST_LAST_WEEK_PRORATION = "first_last_week_proration" # permissible: 541.602(b)(6)
FMLA_LEAVE = "fmla_leave" # permissible: 541.602(b)(7)
PARTIAL_DAY_DISCIPLINE = "partial_day_discipline" # improper
LACK_OF_WORK = "lack_of_work" # improper
QUALITY_OR_QUANTITY = "quality_or_quantity" # improper
PERMISSIBLE_REASONS = frozenset({
DeductionReason.FULL_DAY_PERSONAL,
DeductionReason.FULL_DAY_SICKNESS_PLAN,
DeductionReason.JURY_WITNESS_MILITARY_OFFSET,
DeductionReason.SAFETY_RULE_PENALTY,
DeductionReason.CONDUCT_SUSPENSION_FULL_DAY,
DeductionReason.FIRST_LAST_WEEK_PRORATION,
DeductionReason.FMLA_LEAVE,
})
@dataclass(frozen=True)
class DeductionEvent:
reason: DeductionReason
amount: Decimal
is_full_day: bool
@dataclass(frozen=True)
class CompRecord:
employee_id: str
weekly_salary: Decimal
weekly_threshold: Decimal
deduction_events: List[DeductionEvent] = field(default_factory=list)
duties_test_result: str = "UNDETERMINED"
has_communicated_policy: bool = False
deduction_reimbursed: bool = False
Step 2 — Evaluate the salary-level test. Compare in Decimal with an inclusive floor, since 29 CFR § 541.600 sets a minimum the salary must meet or exceed. Expected output: a weekly salary equal to the threshold passes.
def salary_level_passes(record: CompRecord) -> bool:
"""29 CFR 541.600 — salary must meet or exceed the resolved weekly floor."""
return record.weekly_salary >= record.weekly_threshold
# weekly_salary = Decimal("1128.00"), weekly_threshold = Decimal("1128.00")
# salary_level_passes(record) -> True
Step 3 — Detect improper deductions and apply the actual-practice test. A single improper, full-day-equivalent deduction with a communicated policy and a reimbursement is cured by the § 541.603 safe harbor; anything else with an improper deduction fails the basis test. Expected output: one improper partial-day deduction, uncured, returns False.
def salary_basis_passes(record: CompRecord) -> bool:
"""29 CFR 541.602 with the 541.603 safe harbor for isolated, cured deductions."""
improper = [
e for e in record.deduction_events
if e.reason not in PERMISSIBLE_REASONS
]
if not improper:
logger.info(
"sbt_basis_check emp=%s improper_count=0 result=pass",
record.employee_id,
)
return True
isolated = len(improper) == 1
cured = (
isolated
and record.has_communicated_policy
and record.deduction_reimbursed
)
logger.info(
"sbt_basis_check emp=%s improper_count=%s isolated=%s cured=%s",
record.employee_id, len(improper), isolated, cured,
)
return cured
# One partial-day discipline deduction, no policy, no reimbursement:
# salary_basis_passes(record) -> False
Step 4 — Combine the tests into the classification gate. Salary level and salary basis are decided in code; duties is consumed as a flag, never inferred. Expected output for a record passing level and basis but with duties_test_result="UNDETERMINED": status PENDING_REVIEW.
class ClassificationStatus(str, Enum):
EXEMPT = "exempt"
NON_EXEMPT = "non_exempt"
PENDING_REVIEW = "pending_review"
def classify_exempt_status(record: CompRecord) -> ClassificationStatus:
if not salary_level_passes(record):
status = ClassificationStatus.NON_EXEMPT
logger.info(
"flsa_sbt_classify emp=%s status=%s reason=salary_level",
record.employee_id, status.value,
)
return status
if not salary_basis_passes(record):
status = ClassificationStatus.NON_EXEMPT
logger.info(
"flsa_sbt_classify emp=%s status=%s reason=salary_basis",
record.employee_id, status.value,
)
return status
if record.duties_test_result == "FAIL":
status = ClassificationStatus.NON_EXEMPT
elif record.duties_test_result == "PASS":
status = ClassificationStatus.EXEMPT
else:
status = ClassificationStatus.PENDING_REVIEW
logger.info(
"flsa_sbt_classify emp=%s status=%s reason=duties_%s",
record.employee_id, status.value, record.duties_test_result.lower(),
)
return status
Step 5 — Run the gate end to end. Feed a record that clears the floor, carries one cured improper deduction, and has a confirmed duties pass.
record = CompRecord(
employee_id="E-70213",
weekly_salary=Decimal("1150.00"),
weekly_threshold=Decimal("1128.00"),
deduction_events=[
DeductionEvent(
reason=DeductionReason.PARTIAL_DAY_DISCIPLINE,
amount=Decimal("45.00"),
is_full_day=False,
)
],
duties_test_result="PASS",
has_communicated_policy=True,
deduction_reimbursed=True,
)
result = classify_exempt_status(record)
# result is ClassificationStatus.EXEMPT — the isolated, reimbursed deduction
# is cured by the 541.603 safe harbor, so it does not reach salary-basis failure.
Verification
Run these cases before the gate feeds any overtime or accrual routine:
- Salary-level boundary. Set
weekly_salary == weekly_thresholdand assertsalary_level_passesreturnsTrue; set it one cent below and assertFalse. The test is inclusive, matching the>=in FLSA Threshold Mapping. - Uncured improper deduction flips the result. Feed a record with one
PARTIAL_DAY_DISCIPLINEevent,has_communicated_policy=False, and assertclassify_exempt_statusreturnsNON_EXEMPTeven thoughweekly_salaryclears the floor by a wide margin. - Safe harbor cures an isolated deduction. Feed the same improper event with
has_communicated_policy=Trueanddeduction_reimbursed=True, and assertsalary_basis_passesreturnsTrue. Then add a second improper event in the same period and assert it returnsFalse— the safe harbor only reaches truly isolated deductions. - Permissible deductions never trip the detector. Feed a
FULL_DAY_SICKNESS_PLANevent and assertsalary_basis_passesreturnsTruewith zero improper deductions logged. - Duties is a flag, not an inference. Feed a record passing level and basis with
duties_test_result="UNDETERMINED"and assert the gate returnsPENDING_REVIEW, neverEXEMPTby default. - Decimal determinism. Run case 1 three times and assert byte-identical booleans; any drift means a
floatentered the comparison path.
Failure Modes
- Auto-exempting on salary level alone. Root cause: the gate short-circuits after
salary_level_passesreturnsTrueand skips the basis and duties checks entirely, so a docked, hourly-equivalent employee is reported exempt. Remediation: enforce the three-stage sequence as a single function with no early success return before all applicable checks run, and add a regression test that assertsEXEMPTis unreachable without an explicitduties_test_result="PASS". - Treating every improper deduction as fatal. Root cause: the detector flags the exemption as lost on the first improper deduction it sees, ignoring the § 541.603 safe harbor, which turns a single corrected payroll error into a mass reclassification event and a retroactive overtime liability that was never actually owed. Remediation: implement the isolated-plus-cured branch explicitly, and log
improper_count,isolated, andcuredseparately so the actual-practice pattern is auditable across pay periods, not just within one. - Missing communicated-policy evidence at safe-harbor time. Root cause:
has_communicated_policydefaults toTruewhen the field is absent from the HRIS feed, silently granting a safe harbor the employer never actually established. Remediation: require the field explicitly, quarantine records where it is null via Fallback Routing Strategies, and never treat an unset flag as satisfied.
Frequently Asked Questions
Does one improper deduction always destroy an employee's exempt status?
Not by itself. Under the actual-practice doctrine in 29 CFR § 541.603, an isolated or inadvertent improper deduction is cured if the employer has a clearly communicated policy prohibiting improper deductions — including a complaint mechanism — and reimburses the employee. What destroys the exemption is a pattern: repeated improper deductions across pay periods or employees, evaluated by factors like the number of improper deductions relative to infractions and the time period involved.
What deductions are permissible under the salary-basis test?
29 CFR § 541.602(b) enumerates a closed list: full-day absences for personal reasons or bona fide sickness plans, offsets for jury or witness fees and military pay, penalties for major safety-rule violations, full-day disciplinary suspensions for workplace conduct violations under a written policy, proportional pay in the first or last week of employment, and unpaid FMLA leave. Any deduction outside that list — most notably docking pay for a partial-day absence — is improper regardless of how small the amount is.
If a salary clears the federal threshold, is the employee automatically exempt?
No. Clearing the salary-level test under 29 CFR § 541.600 is one of three required conditions. The employee must also be paid on a true salary basis under § 541.602 and satisfy the applicable duties test under § 541.700. A classification gate that returns exempt on salary level alone is reporting an unverified determination, which is why this implementation routes an unresolved duties result to a pending-review state instead of a default pass.
How is the salary-basis test different from the salary-level test?
The salary-level test is a single arithmetic comparison: does the annualized or weekly salary meet or exceed a fixed floor. The salary-basis test is behavioral: does the employer actually pay that salary as a predetermined amount without reducing it for variations in the quality or quantity of work. An employee can clear the level test every pay period and still fail the basis test the moment an improper, uncured deduction hits their pay.
Related
- FLSA Threshold Mapping — the parent topic that resolves the effective-dated salary floor this gate compares against.
- Mapping FLSA Thresholds for Multi-State Payroll — tiered override resolution for employees whose worksite threshold varies by jurisdiction.
- ACA Measurement and Stability Periods — another effective-dated compliance gate that must run independently of overtime eligibility.
- Overtime Calculation Engines — the downstream topic this gate feeds: only a non-exempt determination reaches overtime calculation at all.