Skip to content

Treat a Late Check-In as a Check-Out ​

This Groovy script fixes Time Attendance lines where the attendance machine recorded a check-out as a check-in. Any lone check-in made at or after a cut-off time that you choose is moved to the check-out side of the line.

Recommended pattern

This script is not tied to any customer's setup. You can paste it as it is and control it through its parameter.

Class nameEATimeAttendanceMakeInOutIfBeforeTime
ScriptEATimeAttendanceMakeInOutIfBeforeTime.groovy
Runs onTime Attendance, on the Pre Update Calculated Fields event
UsesParameter 2. Leave Parameter 1 empty.
Shipped asSource to paste. It is not built into the system.

When to use it ​

Many attendance machines record every punch the same way and leave it to the import to decide which is which. When an employee forgets to punch in, the only punch of the day is the one at the end of the shift, and it arrives in the Attendance grid as a check-in: In 06-10-2026 17:05, with no Out. The employee then looks like someone who arrived at five in the evening and never left.

If your shifts start in the morning, a lone punch in the afternoon can only be a check-out. This script applies that rule for you: give it a cut-off time such as 12:00, and every lone check-in at or after 12:00 becomes a check-out.

How it works ​

For every line of the Attendance grid:

  1. A line that already has an Out date is left alone. So is a line with no In date.
  2. If the In time is at or after the cut-off time in Parameter 2, the In date and time are moved to Out, and In is cleared.
  3. Otherwise the line is left alone.

The script runs on Pre Update Calculated Fields, before the system's own calculations, so the document is saved with the corrected line.

Not for night shifts

The rule assumes every real check-in happens before the cut-off. An employee on an evening or night shift who checks in at 16:00 would be turned into a check-out. Do not use this script on documents that include such employees, or choose a cut-off later than any real check-in.

Setting it up ​

  1. Open Administration → Display Customization → Entity Flow and create a new flow.
  2. Set Target Type to Time Attendance and Target Action to Pre Update Calculated Fields.
  3. In the Details grid, paste the whole script into Groovy Script. Leave Class Name empty: it is filled in when you save.
  4. Fill in the parameters, then save the flow.
ParameterTitle shown after savingWhat to enter
Parameter 1Not used - leave emptyNothing.
Parameter 2Cut Off Time (eg. 11:30)The cut-off time as hours and minutes on a 24-hour clock, for example 12:00. This parameter is required.

Example ​

With Parameter 2 set to 12:00, an import brings these two lines for one employee:

LineInOut
106-10-2026 17:05(empty)
207-10-2026 08:10(empty)

The document is saved as:

LineInOut
1(empty)06-10-2026 17:05
207-10-2026 08:10(empty)

Line 1 was after the cut-off, so it became a check-out. Line 2 was before it, so it stayed a check-in.

Messages you may see ​

The script raises no messages of its own. It changes lines silently, so check the Attendance grid after saving if you are not sure what it did.

The script ​

groovy
package com.namasoft.modules.humanresource.utils.actions


import com.namasoft.infra.domainbase.datafields.LongTextDF
import com.namasoft.infra.domainbase.datafields.TimeDF
import com.namasoft.infra.domainbase.entity.base.EntityAction
import com.namasoft.infra.domainbase.util.Result
import com.namasoft.modules.humanresource.domain.entities.TimeAttendance

public class EATimeAttendanceMakeInOutIfBeforeTime implements EntityAction<TimeAttendance> {
    @Override
    Result doAction(TimeAttendance object, LongTextDF... parameters) {
        Result result = Result.createAccumulatingResult();
        TimeDF cuttOf = TimeDF.fromString(parameters[1].getPrimitiveValue());
        for (final def line in object.getAttendanceLines()) {
            if (line.getToDate() != null)
                continue;
            if (line.getFromDate() == null)
                continue;
            if (line.getFromTime() >= cuttOf) {
                line.setToDate(line.getFromDate());
                line.setToTime(line.getFromTime());
                line.setFromDate(null);
                line.setFromTime(TimeDF.EMPTY());
            }
        }
        return result;
    }

    @Override
    String describe() {
        return "Time Attendance: an attendance line that has an In date but no Out date, and whose In time is at or after the cut-off time " +
                "in parameter 2, is turned into a check-out: its In date and time move to Out, and In is cleared. " +
                "Lines that already have an Out date are not changed. " +
                "Attach it to the Pre Update Calculated Fields event of Time Attendance. Parameter 1 is not used.";
    }

    @Override
    List<String> columnNames() {
        return Arrays.asList("Not used - leave empty", "Cut Off Time (eg. 11:30)")
    }
}