Skip to content

fix: push the Google Sheet headers when a worker is created - #1281

Merged
renemadsen merged 1 commit into
stablefrom
fix/push-headers-on-worker-create
Sep 17, 2026
Merged

renemadsen merged 1 commit into
stablefrom
fix/push-headers-on-worker-create

Conversation

@renemadsen

Copy link
Copy Markdown
Member

Problem

A worker created in BackendConfiguration with time registration enabled gets an AssignedSite, but no columns in the TimePlanning Google Sheet.

CreateDeviceUser creates the assignment at BackendConfigurationAssignmentWorkerServiceHelper.cs:1843 and stops there. UpdateDeviceUser does the very same thing at line 1403 and then pushes the headers on line 1404 (and again at 1222 for the "assignment already exists" branch). Nothing else syncs headers — there is no scheduled job for it in any repo.

Because the import matches columns by name, a worker with no column is never imported at all. Their columns appear only once somebody edits them again, or saves the TimePlanning settings page. After #1300 the scheduled import does log it ("site … imports from the sheet but no column names it"), but only at info level.

Fix

CreateDeviceUser now pushes the headers, right after the assignment is created, inside the TimeRegistrationEnabled branch. The call is wrapped in try/catch with a log and a Sentry event: it reaches a third-party API and reads a plugin setting a tenant may not have (PushToGoogleSheet does .Single(...) on PluginConfigurationValues before its env-var check), and neither may cost us the worker. CreateDeviceUser gained an optional ILogger? logger = null parameter, so its existing callers — several in BackendConfiguration.Pn.Integration.Test — keep compiling.

PushToGoogleSheet gets the same fix as the timeplanning plugin's copy (#1714), which is where tenant 1063's split pairs came from:

  • Headers are matched ignoring case, whitespace and dash style rather than by exact text, so a hand-edited header isn't duplicated.
  • A site with only half a pair gets a complete new pair, and the column left behind is named in a warning to the log and Sentry.
  • Only the appended cells are written, instead of rewriting the whole header row.
  • Appends never land in the date columns the import skips.
  • The rename path is unchanged in behaviour; it now matches the renamed site the same normalized way.

Tests

PlanTimerSheetColumnsTests (new, pure unit) covers the mapping and the append planning. This repo runs the whole unit-test project, so no allowlist entry is needed.

Verified locally: dotnet build of BackendConfiguration.Pn, BackendConfiguration.Pn.Test and BackendConfiguration.Pn.Integration.Test. Tests run in CI.

Note

PlanTimerSheetColumns.cs here is a hand-kept copy of the same file in the timeplanning plugin, which is itself a copy of the scheduled import's. The three must agree or the same sheet is read and written differently through each path. Consolidating them into Microting.TimePlanningBase is the real fix and needs a base release.

🤖 Generated with Claude Code

A worker created with time registration enabled got an AssignedSite but
no columns in the PlanTimer sheet: CreateDeviceUser never pushed the
headers, while UpdateDeviceUser has always pushed right after creating
the same row. The import matches columns by name, so such a worker was
never imported at all -- their columns only appeared once somebody
edited them again or saved the TimePlanning settings page.

CreateDeviceUser now pushes too. The call is guarded: it reaches a
third-party API and reads a plugin setting a tenant may not have, and
neither may cost us the worker.

PushToGoogleSheet gets the same fix as the timeplanning plugin's copy:
headers are matched ignoring case, whitespace and dash style rather than
by exact text, a site missing half a pair gets a complete new pair and a
reported column to clean up, only the appended cells are written, and
appends never land in the date columns the import skips.

PlanTimerSheetColumns is a hand-kept copy of that plugin's file, which
is itself a copy of the scheduled import's. Consolidating the three into
Microting.TimePlanningBase needs a base release and is left for later.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 17, 2026 12:02

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

New unit tests currently contradict the ParseHours implementation and the Google Sheet rename-write path can still throw on an empty header row, which can break updates in valid scenarios.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR fixes a time-registration edge case in the BackendConfiguration plugin where newly created workers could get an AssignedSite but still have no corresponding - timer / - tekst columns in the TimePlanning Google Sheet, preventing them from being imported (imports match columns by name).

Changes:

  • Extend CreateDeviceUser to push PlanTimer sheet headers immediately after creating the assignment (errors are caught/logged so worker creation is not blocked).
  • Rework PushToGoogleSheet header handling to plan/apply only needed header appends (normalized matching, avoid full-row rewrites) and report inconsistencies via log + Sentry.
  • Add PlanTimerSheetColumns helper plus new unit tests covering header mapping and append planning.
File summaries
File Description
eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Services/BackendConfigurationAssignmentWorkerService/BackendConfigurationAssignmentWorkerService.cs Passes logger through to helper so CreateDeviceUser can safely log sheet-push failures.
eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Infrastructure/Helpers/PlanTimerSheetColumns.cs Introduces normalized header mapping + append planning for PlanTimer columns.
eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Infrastructure/Helpers/GoogleSheetHelper.cs Uses PlanTimerSheetColumns to append only missing headers and logs/sends Sentry warnings for layout problems.
eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn/Infrastructure/Helpers/BackendConfigurationAssignmentWorkerServiceHelper.cs Adds optional ILogger and pushes sheet headers on worker creation (guarded try/catch).
eFormAPI/Plugins/BackendConfiguration.Pn/BackendConfiguration.Pn.Test/PlanTimerSheetColumnsTests.cs Adds focused unit coverage for mapping, normalization, and append planning behavior.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +151 to 154
var columnLetter = PlanTimerSheetColumns.ColumnLetter(existingHeaders.Count - 1);
var updateHeaderRequest =
service.Spreadsheets.Values.Update(updateRequest, googleSheetId, $"{sheetName}!A1:{columnLetter}1");
updateHeaderRequest.ValueInputOption =
Comment on lines +217 to +221
if (!double.TryParse(text.Replace(",", "."), NumberStyles.AllowDecimalPoint,
NumberFormatInfo.InvariantInfo, out var hours) || !double.IsFinite(hours))
{
return null;
}
@renemadsen
renemadsen merged commit b9b7ffa into stable Sep 17, 2026
32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants