This article describes how the Payroll Engine is used to develop a payroll solution based on a variable timesheet.
The Payroll Engine is an open source project that enables the development of scalable cloud payroll solutions.

The following requirements were considered:

  • Support for different payroll periods: weekly, bi-weekly, monthly, bi-monthly, etc.
  • Wage factor for casual workers.
  • Variable daily rates divided into regular working hours and early/late periods.
  • Parallel use of timesheets with different working time models.
  • Timesheet data can be changed daily, scheduled and time limited.
  • Customization of wage calculation with individual additional rates and factors.
  • Automated payroll testing.
  • Definition of special days (public holidays, trade fair, etc.).
  • Integration of external working hours from Excel documents.
  • Report on employee working hours and wages by period.

Use Cases

The application covers the following use cases:

  • Timesheet: Configure timesheet
  • Employment: Manage employee employment
  • Work Time: Record employee work time
  • Payrun: Perform wage calculation
  • Wage Report: Create employee wage document
  • Work Time Report: Create employee working time report

Timesheet Payroll Use Cases

Working times are recorded by the employee themselves (self-service) or by the HR user.

Timesheet

The timesheet describes the working time and basic wage data:

Field Description Value type
StartTime Regular period start time Hour
EndTime Regular period end time Hour
MinWorkTime Minimum work time Hour
MaxWorkTime Maximum work time Hour
BreakMin Minimum break time Minute
BreakMax Maximum break time Minute
RegularRate Regular hour rate Money
CasualRateFactor Casual worker factor to the regular rate Percent

Time Periods

In the timesheet, the working day is divided into different time periods:

  • Regular period: Start and end of the working day and pay rates
  • Early periods: Working periods from midnight to the start of regular working hours
  • Late periods: Working periods after the regular working time until midnight

Timesheet Periods

The periods are arranged according to the sort key next to the standard working time. Negative sort values apply to early periods and positive sort values to late periods.

The following standard fields exist for each period:

Field Description Value type
Duration Period duration Hour
Factor Period rate factor Percent

Work Time

The employee's working time is defined in the WorkTime object with the following standard fields:

Field Description Value type
WorkTimeDate Working day Date
WorkTimeStart Start hour Hour
WorkTimeEnd End hour Hour
WorkTimeBreak Break time Minute
WorkTimeHours Working hours (calculated) Minute

Wage Calculation

To calculate the daily wage, the average working time is divided into different periods. The period wage is calculated according to the duration of work within the period and the wage factors. The total daily wage is calculated from the wage of the regular working time and all early and late periods.

The following example shows the distribution of the employee's working time between the daily periods. The employee has worked from 9.00 to 20.00 with a break of 30 minutes:

Timesheet Wage Calculation

Application Usage

The use cases are executed in the Payroll Engine web application:

First, the timesheet is defined using the Timesheet use case and the employee's employment type is defined using the Employment use case. The employee's working and break times are then recorded using the Work Time use case:

Timesheet Work Time Case

Break times are optional and can be hidden. Authorized users can also run these cases for forecast scenarios.
When a pay run job is executed, the payroll data for the employees is created and saved as Payroll Results.

The employees' working times and the payroll results can be downloaded in various formats (pdf, excel, json, xml).

Timesheet Reports

Special days import

Special days, such as public holidays to be excluded or weekend days to be included, such as trade fairs, are managed in the Workday lookup. The special days can also be imported from an Excel file using the Payroll Console (see example Lookup/Workday/Workdays.2025.xlsx).

Working hours import

For employees who do not have online access, the Payroll Console can also be used to import the working times of employees who have Online (see example Case.Data/WorkingTimes.2025.Week8.xlsx).

Implementation

Tenant Setup

The payroll calendar, which determines the payroll cycle and the regular working days, is defined in the client. The users are assigned to divisions. The calendar determines the payroll cycle and the working days. Employees, who are also entered as users, can record and query their working time.

Timesheet Tenant

The payroll data is managed in the regulation object:

  • Lookup tables such as the special days.
  • Cases and their fields, which define the data model.
  • Wage types and collectors for the wage run.
  • Evaluation with reports.

👉 Tenants can be imported and updated as Json with the Payroll Console (see Wiki Basic Payroll).

Timesheet Scripting

A timesheet is defined by deriving the Timesheet class. The timesheet periods are listed as members of the class and marked with the TimesheetPeriod attribute. This determines whether it is an early or late shift and the order in which they are listed. The attribute parameter ns stands for namespace and is used as a prefix for the case fields of the periods.

public class MyTimesheet : Timesheet
{
    // case fields EarlyPeriodDuration and EarlyPeriodFactor
    [TimesheetPeriod(order: -1, ns: nameof(EarlyPeriod))]
    public TimesheetPeriod EarlyPeriod { get; } = new();

    // case fields LatePeriodLowDuration and LatePeriodLowFactor
    [TimesheetPeriod(order: +1, ns: nameof(LatePeriodLow))]
    public TimesheetPeriod LatePeriodLow { get; } = new();

    // case fields LatePeriodHighDuration and LatePeriodHighFactor
    [TimesheetPeriod(order: +2, ns: nameof(LatePeriodHigh))]
    public TimesheetPeriod LatePeriodHigh { get; } = new();
}

In the following example, an additional field WeekendRateFactor is added to the timesheet to allow for an additional surcharge on weekend days.

public class MyTimesheet : Timesheet
{
    // case fields LatePeriodDuration and LatePeriodFactor
    [TimesheetPeriod(order: +1, ns: nameof(LatePeriod))]
    public TimesheetPeriod LatePeriod { get; } = new();

    // case field WeekendRateFactor
    public decimal WeekendRateFactor { get; set; }
}

User-defined wage calculation

A derivative of the TimesheetCalculator class is implemented for the wage calculation. The CalcRegularWage method takes into account the additional weekend factor in the wage calculation:

public class MyTimesheetCalculator : TimesheetCalculator<MyTimesheet>
{
    protected override decimal CalcRegularWage(WageDay day, HourPeriod timesheetPeriod)
    {
        // regular wage
        var wage = base.CalcRegularWage(day, timesheetPeriod);

        // weekend factor
        if (day.Date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday)
        {
            wage *= (1m + day.Timesheet.WeekendRateFactor);
        }
        return wage;
    }
}

The WeekDay parameter contains the daily values of the timesheet, the employment type and the daily working time.

Multiple Timesheets

To use multiple timesheets, the namespace can be defined with the CaseObject attribute:

[CaseObject(ns: "Int")]
public class IntTimesheet : Timesheet
{
    // case fields IntEarlyPeriodDuration and IntEarlyPeriodFactor
    [TimesheetPeriod(order: -1, ns: nameof(EarlyPeriod))]
    public TimesheetPeriod EarlyPeriod { get; } = new();

    // case fields IntLatePeriodDuration and IntLatePeriodFactor
    [TimesheetPeriod(order: +1, ns: nameof(LatePeriod))]
    public TimesheetPeriod LatePeriod { get; } = new();
}

[CaseObject(ns: "Ext")]
public class ExtTimesheet : Timesheet
{
    // case fields ExtEarlyPeriodDuration and ExtEarlyPeriodFactor
    [TimesheetPeriod(order: -1, ns: nameof(EarlyPeriod))]
    public TimesheetPeriod EarlyPeriod { get; } = new();

    // case fields ExtLatePeriodDuration and ExtLatePeriodFactor
    [TimesheetPeriod(order: +1, ns: nameof(LatePeriod))]
    public TimesheetPeriod LatePeriod { get; } = new();
}

Scripting

The scripts in the Payroll Engine are used to control the runtime behavior and are executed on the Backerd server.

Case Scripting

For cases, scripts can be used to control file input (Case Build) and data storage (Case Validate). The Cases worksheet contains the following scripts:

Timesheet Case Scripting

The following script checks the data in the Timesheet case:

public static bool Validate<TTimesheet>(CaseValidateFunction function)
    where TTimesheet : Timesheet, new()
{
    var workday = function.GetChangeCaseObject<TTimesheet>();

    // retro changes
    if (!function.AdminUser)
    {
        var start = function.GetStart(nameof(Timesheet.RegularRate));
        var period = function.GetCalendarPeriod();
        if (start.HasValue && period.IsBefore(start.Value))
        {
            function.AddIssue($"Timesheet change date {start.Value:d} is before calendar start {period.Start:d}");
            return true;
        }
    }

    // regular work time
    var workTime = workday.EndTime - workday.StartTime;
    if (workTime <= 0)
    {
        function.AddIssue("Missing timesheet regular duration.");
        return true;
    }
    if (workday.MaxWorkTime < workday.MinWorkTime)
    {
        function.AddIssue("Invalid working time maximum.");
        return true;
    }

    // break
    if (workday.BreakMax / 60 >= workTime)
    {
        function.AddIssue("Break time maximum must be less than the regular working time.");
        return true;
    }
    if (workday.BreakMax < workday.BreakMin)
    {
        function.AddIssue("Invalid break time maximum.");
        return true;
    }

    // time step
    if (workday.WorkTimeStep is < 1 or > 30)
    {
        function.AddIssue("Invalid working time step size.");
        return true;
    }
    if (60m % workday.WorkTimeStep != 0)
    {
        function.AddIssue("Working time step must be a part of 60 minutes.");
        return true;
    }

    // start/end date
    function.UpdateStart(function.GetStart("RegularRate"));
    function.UpdateEnd(function.GetEnd("RegularRate"));
    return true;
}

The script checks the following aspects of the timesheet (areas):

  • retro changes: No time recording outside the working period, which can be overridden by the administrator.
  • regular work time: Valid limits of the regular work time.
  • break: Valid break time limits.
  • time step: Valid step size for the input accuracy.
  • start/end date: Ensures that all timesheet fields apply to the same period.

The timesheet case functions are located in the classes Timesheet/CaseBuild.cs and Timesheet/CaseValidate.cs.

Wage Type Scripting

The calculation of wages is divided into wage types, which are processed in numerical order. To display wages for different periods, a wage type is assigned to each period:

Timesheet Wage Type Scripting

The timesheet wage type functions are located in the classes Timesheet/WageTypeValue.cs.

Report Scripting

In reports, the scripts control the file input (Report Build) and the document generation (Report End).

Timesheet Report Scripting

The Timesheet Wage Type functions are located in the classes Timesheet/ReportBuild.cs and Timesheet/ReportEnd.cs.

Local Development

The Payroll Engine provides the ability to develop and debug case and report scripts locally. Based on the PayrolLEngine.Client.Services, the backend runtime environment is mapped locally. This requires the Payroll Engine API to be available:

Payroll Engine Local Development

Testing

This example includes two tests on different employees. The tests Test/Test.Employee1.pecmd and Test/Test.Employee2.pecmd are run from the Payroll Console:

  1. create a test copy of the employee (name test n), which will be used in the following.
  2. run the Work Time case for 5 working days.
  3. run the payroll for this week.
  4. check the values of the wage types and collectors.

The timing of use cases and payload runs can be freely defined to reflect past or future test scenarios.

👉 Read more about Payroll Testing.

Installation

To use the timesheet application, the Payroll Engine must be installed locally:

  1. Download, install and run the backend API.
  2. Install the timesheet payroll (folder Examples/TimesheetPayroll).
  3. Run the timesheet use cases
    • in the web application (uires the web app server to be started).
    • with the Payroll Console (Json documents).

More information:
👉 Payroll Engine Wiki

👉 ReadMe TimesheetPayroll

👉 Payroll Engine Client Service NuGet