Reject changing the member of running time entries

Moving a running time entry to another member could give that member's
user a second running time entry. Stopping the time entry and changing
the member in the same request is still allowed.
This commit is contained in:
Constantin Graf
2026-10-13 17:08:25 +02:00
parent c791057f25
commit 332edcb047
5 changed files with 151 additions and 1 deletions

View File

@@ -11,6 +11,7 @@ use App\Exceptions\Api\FeatureIsNotAvailableInFreePlanApiException;
use App\Exceptions\Api\OverlappingTimeEntryApiException;
use App\Exceptions\Api\PdfRendererIsNotConfiguredException;
use App\Exceptions\Api\TimeEntryCanNotBeRestartedApiException;
use App\Exceptions\Api\TimeEntryMemberCanNotBeChangedWhileRunningApiException;
use App\Exceptions\Api\TimeEntryStillRunningApiException;
use App\Http\Requests\V1\TimeEntry\TimeEntryAggregateExportRequest;
use App\Http\Requests\V1\TimeEntry\TimeEntryAggregateRequest;
@@ -668,7 +669,7 @@ class TimeEntryController extends Controller
*
* To stop a running timer, set `end` to the stop time (UTC). Times the user gives in their own timezone must be converted to UTC first.
*
* @throws AuthorizationException|TimeEntryCanNotBeRestartedApiException
* @throws AuthorizationException|TimeEntryCanNotBeRestartedApiException|TimeEntryMemberCanNotBeChangedWhileRunningApiException
*
* @operationId updateTimeEntry
*/
@@ -694,6 +695,11 @@ class TimeEntryController extends Controller
$effectiveEnd = $request->has('end') ? ($request->input('end') !== null ? Carbon::parse($request->input('end')) : null) : $timeEntry->end;
$this->assertNoOverlap($organization, $effectiveMember, $effectiveStart, $effectiveEnd, $timeEntry);
// The new member could already have a running time entry
if ($effectiveEnd === null && $newMember !== null && $newMember->getKey() !== $timeEntry->member_id) {
throw new TimeEntryMemberCanNotBeChangedWhileRunningApiException;
}
$oldProject = $timeEntry->project;
$oldTask = $timeEntry->task;
@@ -819,6 +825,12 @@ class TimeEntryController extends Controller
continue;
}
// The new member could already have a running time entry
if ($newMember !== null && $timeEntry->end === null && $newMember->getKey() !== $timeEntry->member_id) {
$error->push($id);
continue;
}
$oldProject = $timeEntry->project;
$oldTask = $timeEntry->task;