Refactor transaction handling and improve shift time logic

Introduced a new `getAllTransactionIds` utility function to better handle filtering of transaction IDs. Replaced outdated `start`/`end` time properties with `checkIn`/`checkOut` objects for shift records, alongside added validation in tests to exclude shifts without punches. Enhanced invoicing tests to ensure flags remain visible even for excluded transactions.
This commit is contained in:
Jeppe Bundgaard
2026-05-13 12:19:07 +02:00
parent 5965c5d72d
commit 30ed01e717
2 changed files with 36 additions and 15 deletions
@@ -22,13 +22,12 @@ final class workfeed_shift_time_resolver
}
$actual_start = self::firstDateTimeFromPaths($record, [
'checkIn.time',
'checkIn',
'actualStart',
'actualStartTime',
'clockIn',
'clockInTime',
'start',
'startTime',
'from',
'approval.originalStart',
]);
$scheduled_end = self::firstDateTimeFromPaths($record, [
@@ -38,17 +37,14 @@ final class workfeed_shift_time_resolver
'to',
]);
$actual_only_end = self::firstDateTimeFromPaths($record, [
'checkOut.time',
'checkOut',
'actualEnd',
'actualEndTime',
'clockOut',
'clockOutTime',
]);
$current_end = self::firstDateTimeFromPaths($record, [
'end',
'endTime',
'to',
]);
$saved_actual_end = $actual_only_end ?? $current_end;
$saved_actual_end = $actual_only_end;
if ($actual_start === null || $scheduled_end === null || $saved_actual_end === null) {
return null;
@@ -23,22 +23,26 @@ it('calculates workfeed employee hours for the hour slot based on overlap', func
$shifts = [
(object)[
'departmentID' => 'dep_1',
'start' => '2026-03-24T13:00:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T13:00:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T14:00:00+00:00'],
'end' => '2026-03-24T14:00:00+00:00',
],
(object)[
'departmentID' => 'dep_1',
'start' => '2026-03-24T13:30:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T13:30:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T15:00:00+00:00'],
'end' => '2026-03-24T15:00:00+00:00',
],
(object)[
'departmentID' => 'dep_other',
'start' => '2026-03-24T13:00:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T13:00:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T14:00:00+00:00'],
'end' => '2026-03-24T14:00:00+00:00',
],
(object)[
'departmentID' => 'dep_1',
'start' => '2026-03-24T14:00:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T14:00:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T13:00:00+00:00'],
'end' => '2026-03-24T13:00:00+00:00',
],
];
@@ -93,12 +97,14 @@ it('calculates workfeed employee hours across multiple departments for one hour
$shifts = [
(object)[
'departmentID' => 'dep_1',
'start' => '2026-03-24T13:00:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T13:00:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T14:00:00+00:00'],
'end' => '2026-03-24T14:00:00+00:00',
],
(object)[
'departmentID' => 'dep_2',
'start' => '2026-03-24T13:00:00+00:00',
'checkIn' => (object)['time' => '2026-03-24T13:00:00+00:00'],
'checkOut' => (object)['time' => '2026-03-24T14:00:00+00:00'],
'end' => '2026-03-24T14:00:00+00:00',
],
];
@@ -108,6 +114,25 @@ it('calculates workfeed employee hours across multiple departments for one hour
expect($hours)->toBe(2.0);
});
it('does not count shifts without punches when checkIn/checkOut are null', function (): void {
$route = new moduleWeatherAPIRoute();
$slot = new DateTime('2026-03-24T13:00:00+00:00');
$shifts = [
(object)[
'departmentID' => 'dep_1',
'checkIn' => null,
'checkOut' => null,
'start' => '2026-03-24T13:00:00+00:00',
'end' => '2026-03-24T14:00:00+00:00',
],
];
$hours = weather_route_invoke_private($route, 'calculateWorkfeedEmployeeHoursForHour', [$shifts, 'dep_1', $slot]);
expect($hours)->toBe(0.0);
});
it('counts overtime minutes when a saved shift end extends past the approved original end', function (): void {
$route = new moduleWeatherAPIRoute();
$slot = new DateTime('2026-03-23T18:00:00+00:00');