Calculations for one line and one week of the U.S. Department of Labor WH-347 certified payroll form, revised version (Rev. January 2025), used on Davis-Bacon and Related Acts contracts.
Given a worker's week (base rate, fringe rates per plan, straight-time and overtime hours per day, cash in lieu of fringe, deductions), the package computes columns 5 to 9 of the form, per-plan fringe totals for the crew, and a reconciliation check of column 6B. It has no dependencies and no I/O.
R port of the Go package
codeberg.org/tresor2k/tabletemplates-go/wh347
and the Rust crate wh347; the tests replay
every case of the Go tests with the same expected values.
install.packages("wh347", repos = c("https://tresor4k.r-universe.dev", "https://cloud.r-project.org"))
library(wh347)
week <- wh347_worker_week(
base_rate = 30,
fringe = wh347_fringe_rates(health_welfare = 4, pension = 2),
st_by_day = c(8, 8, 8, 8, 8, 0, 0),
ot_by_day = c(4, 0, 0, 0, 0, 0, 0),
deductions = wh347_deductions(other = 310))
line <- wh347_compute_line(week)
line$total_hours # 44 column 5
line$ot_rate # 45 column 6A, OT line
wh347_round_cents(line$fringe_credit) # 264 column 6B
wh347_round_cents(line$gross_project) # 1380 column 7A
wh347_round_cents(line$net_pay) # 1070 column 9
line$sanity # "ok"
run <- wh347_compute_run(list(week))
wh347_round_cents(run$plan_totals$health_welfare) # 176
run$reconciles # TRUE
| Column | Content | In this package |
|---|---|---|
| 1A to 1E | Worker entry number, name, identifying number | not handled (identity) |
| 2 | "J" journeyworker or "RA" registered apprentice |
worker_type of wh347_worker_week(), copied to the line |
| 3 | Labor classification | not handled |
| 4 | Hours worked each day, ST and OT lines |
st_by_day, ot_by_day
|
| 5 | Total hours for the week |
st_hours, ot_hours, total_hours
|
| 6A | Hourly rate paid, ST and OT lines (excluding cash in lieu of fringe) |
st_rate, ot_rate = base rate x 1.5 |
| 6B | Total fringe benefit credit |
fringe_credit = total hours x total hourly fringe credit |
| 6C | Payment in lieu of fringe benefits |
cash_in_lieu = total hours x cash_in_lieu_rate
|
| 7A | Gross amount earned, this project |
gross_project = ST x rate + OT x rate x 1.5 + 6C |
| 7B | Gross amount earned, all work |
gross_all_work = entered value (gross_all_work not NULL), else 7A |
| 8 | Deductions for all work |
total_deductions = sum of the itemized deductions |
| 9 | Net payment for all work |
net_pay = 7B - 8 (the 6B fringe credit is never included) |
sanity is "review" when the net pay is negative, the week exceeds 112 hours,
the base rate is negative, or an entered 7B is lower than 7A.
Arithmetic is plain double precision with no intermediate rounding, in the same
order of operations as the Go package. wh347_round_cents() rounds to the cent
(halves toward +Inf, like the Go RoundCents; not round()) and is meant for
display only. The ST/OT split is entered by the user, not computed from a daily
or weekly threshold.
Not an official DOL tool, not legal advice; you remain responsible for your certified payrolls. The package does not check wage determinations, fill the Statement of Compliance (page 2), or produce the form itself.
MIT, see LICENSE.md.