Skip to content

Payroll Reports — Payroll Register & Payroll Tax Liability

Date: 2026-07-01

Problem / motivation

Peachtree's payroll section prints per-paycheck and per-tax reports: Payroll Register ("employee paycheck information for the selected date range showing hours, deductions, and paycheck amount") and the Payroll Tax / Tax Liability reports ("adjusted/taxable gross, tax withheld, and liability amounts"). We store calculated payroll in payroll_items (gross/net/bonuses/hours plus deductions and employer_contributions JSONB maps keyed by concept). These two reports surface that data by pay date. Closes Payroll rows on the Peachtree Report Parity tracker. See Payroll for the underlying module.

In scope

  • payroll_register(date_filter, generate_csv) — one row per employee paycheck in the date range (by pay_date): hours, gross, bonuses, total deductions, net, and employer cost.
  • payroll_tax_liability(date_filter) — totals for the range plus a per-concept breakdown of employee deductions (CSS/SE/ISR/…) and employer contributions, summed from the JSONB maps. Covers the Payroll Tax Report and Tax Liability Report intent for Panama (CSS, Seguro Educativo, ISR retention).
  • Path.REPORTS. Register is CSV-exportable.

Out of scope

  • Payroll Journal — payroll postings reach the ledger via payroll_runs.ledger_entry_id but carry no dedicated SourceType; deferred until one exists (would then reuse the journal renderer).
  • Per-check register (printed/unprinted) and vacation/sick accrual — separate payroll data not modeled.
  • Quarterly/Yearly earnings variants — the register already accepts any date range (a quarter/year is just a wider filter), so they are the same report with different bounds.

Data model changes

None. Reads payroll_items (gross, net, bonuses, worked_hours, overtime_hours, deductions, employer_contributions) → payroll_runspayroll_periods (pay_date) and employees. No migration, no new Path/Resource.

What's being implemented

  • Responsesapp/graphql/reports/strawberry/payroll/payroll_report_response.py (PayrollRegisterLineResponse, PayrollTaxLiabilityResponse + concept line type).
  • Repositoryapp/graphql/reports/repositories/payroll/payroll_report_repository.py (payroll_items).
  • Serviceapp/graphql/reports/services/payroll/payroll_report_service.py (sums the JSONB maps).
  • Queriesapp/graphql/reports/queries/payroll/payroll_report_queries.py.
  • Teststests/graphql/reports/test_payroll_reports.py.

GraphQL surface

payrollRegister(dateFilter: DateRangeFilter!, generateCsv: Boolean! = false): GenericReportPage!
payrollTaxLiability(dateFilter: DateRangeFilter!): PayrollTaxLiabilityReport!

type PayrollRegisterLine { employeeNumber: String!, firstName: String!, lastName: String!, payDate: Date!, workedHours: Decimal, overtimeHours: Decimal, gross: Decimal!, bonuses: Decimal!, totalDeductions: Decimal!, net: Decimal!, employerCost: Decimal! }
type PayrollTaxLiabilityReport { startDate: Date!, endDate: Date!, totalGross: Decimal!, totalDeductions: Decimal!, totalEmployerContributions: Decimal!, deductionLines: [PayrollTaxConceptLine!]!, employerContributionLines: [PayrollTaxConceptLine!]! }
type PayrollTaxConceptLine { concept: String!, amount: Decimal! }

Frontend contract

  • Payroll Register: date range over payDate; one row per paycheck. Sum net for the net payroll, totalDeductions for withholdings, employerCost for employer burden. generateCsv: truecsvUrl.
  • Payroll Tax Liability: deductionLines / employerContributionLines are per-concept totals (the JSONB keys) — render as the DGI/CSS liability breakdown; totalGross is the taxable base.

Future additions

  • Payroll Journal via the journal renderer once payroll postings carry a SourceType.
  • Vacation & sick-time and per-check registers when that data is modeled.