You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(snapshot,gps): the start snapshot and position create the day's row when none exists (#1746) - #1752
fix/1746-snapshot-gps-without-plan-rowmicroting/eform-angular-timeplanning-plugin:fix/1746-snapshot-gps-without-plan-rowCopy head branch name to clipboard
fix/1746-snapshot-gps-without-plan-rowmicroting/eform-angular-timeplanning-plugin:fix/1746-snapshot-gps-without-plan-rowCopy head branch name to clipboard
With snapshots enabled, the Start1 snapshot (and the start GPS position) was lost on every day that had no pre-created PlanRegistration. This change creates the day's row when it is missing, so the snapshot and position have a row to point at.
New PlanRegistrationHelper.FindOrCreateDayRowAsync, used by TimePlanningPictureSnapshotService.Create and TimePlanningGpsCoordinateService.Create. It returns the day's row. When there is none, it creates the same empty row the planning gap-fill creates, and carries the predecessor's closing flex balance forward. That keeps the chain intact even if no registration follows. The registration that does follow updates this row like any pre-created one.
A row is only created when all of these hold:
the site has a non-removed AssignedSite (otherwise the call is refused with SiteNotFound);
the day is within one day of today (otherwise the service's generic error);
the day is not inside the reconciled lock.
The snapshot row is resolved before the S3 upload, so a refused snapshot no longer leaves an orphaned file.
The registration that follows can win the insert race. In that case the unique index (SdkSitId, Date, WorkflowState) rejects the second row; the helper detaches it and adopts the existing row instead of failing.
Root cause
The app sends the snapshot and position before the save that creates the row (app side: flutter-time#594). Create then dereferenced planRegistration.Id on null. The exception was caught and logged, and the snapshot was dropped after its file had already been uploaded.
Tests
PictureSnapshotServiceTests:
no row → success, exactly one row is created, the snapshot points to it, and the balance is carried forward;
existing row → linked, no second row;
no AssignedSite → SiteNotFound, nothing created;
five days back → refused, nothing created.
GpsCoordinateServiceTests: the same no-row, no-AssignedSite and out-of-window cases. Also CreateOrGetExistingDayRowAsync on a unique-index violation: it returns the existing row, detaches the rejected one, and the context stays usable.
Not verified
Tests were not run locally; CI runs them.
The locked-day refusal inside the ±1-day window can only trigger when the server's local date differs from the UTC date. It has no dedicated test.
Update in both services still dereferences a missing row; that is outside this issue.
…ow when none exists (#1746)
The app sends the Start1 snapshot and GPS position before the save that
creates the day's PlanRegistration. On a day without a pre-created row,
Create dereferenced a null row: the snapshot was lost (after its file had
been uploaded to S3) and so was the position.
PlanRegistrationHelper.FindOrCreateDayRowAsync resolves the row first and,
when there is none, creates the empty row the planning gap-fill creates,
carrying the predecessor's closing flex balance forward. It only does so for
a site with a time registration (AssignedSite), within a day of today and
outside the reconciled lock; otherwise the call is refused and nothing is
uploaded. If the racing registration wins the insert, the unique index
rejects the second row and the existing one is adopted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…test arrange (#1746)
The new day row carried the predecessor's balance through the decimal
chain even on one-minute sites, replacing the authoritative seconds
balance with its (possibly stale) decimal. It now uses FlexChain.CarryChain
with the site's mode, as the other balance paths do.
The snapshot test helper awaited GetCore() inside Returns(), which let
another substitute call bind the return value; resolve the core first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A request for today.AddDays(1) passes the date check but skips balance carry-forward here. For example, a predecessor balance of 2.5 hours becomes zero on the new row. Unlike the planning gap-fill, these snapshot/GPS callers do not subsequently recompute the row. If no registration follows, the zero balance remains and can seed later rows when FlexChainComputedThrough is null. Run the predecessor lookup and CarryChain for every accepted date, and add a tomorrow-dated regression test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
With snapshots enabled, the Start1 snapshot (and the start GPS position) was lost on every day that had no pre-created
PlanRegistration. This change creates the day's row when it is missing, so the snapshot and position have a row to point at.PlanRegistrationHelper.FindOrCreateDayRowAsync, used byTimePlanningPictureSnapshotService.CreateandTimePlanningGpsCoordinateService.Create. It returns the day's row. When there is none, it creates the same empty row the planning gap-fill creates, and carries the predecessor's closing flex balance forward. That keeps the chain intact even if no registration follows. The registration that does follow updates this row like any pre-created one.AssignedSite(otherwise the call is refused withSiteNotFound);(SdkSitId, Date, WorkflowState)rejects the second row; the helper detaches it and adopts the existing row instead of failing.Root cause
The app sends the snapshot and position before the save that creates the row (app side: flutter-time#594).
Createthen dereferencedplanRegistration.Idon null. The exception was caught and logged, and the snapshot was dropped after its file had already been uploaded.Tests
PictureSnapshotServiceTests:SiteNotFound, nothing created;GpsCoordinateServiceTests: the same no-row, no-AssignedSite and out-of-window cases. AlsoCreateOrGetExistingDayRowAsyncon a unique-index violation: it returns the existing row, detaches the rejected one, and the context stays usable.Not verified
Updatein both services still dereferences a missing row; that is outside this issue.Refs #1746
🤖 Generated with Claude Code