Skip to content

Allow create_time_entry to log time for another user (user_id) #11

Description

@AndreaV-Lsi

Problem / motivation

For agentic project management, we mirror a supplier's reported weekly hours into OpenProject to
track effort against a Time & Materials ceiling. Each entry should be attributed to the person who
did the work (e.g. a Tismo engineer) so their configured hourly rate drives the cost — not the
service/API account the server authenticates as. Today every entry is logged under the authenticated
API user, with no way to attribute it to someone else.

Feasibility spike (Core 17.5.1 — confirmed supported)

  • POST /api/v3/time_entries exists; real entries carry a user link (observed one attributed to a
    different user than the API account).
  • The time-entry schema reports user: writable=True required=True — so _links.user is settable
    on creation. When omitted, OpenProject defaults it to the authenticated user (today's behavior).
  • Logging for another user requires the API user to hold the project permission "Log time for
    other users"
    , and the target must be a project member; otherwise OpenProject returns 403/422.

Scope

  • Tool (src/tools/time_entries.py, CreateTimeEntryInput): add
    user_id: Optional[int] = Field(None, gt=0, description="Log the time on behalf of this user (defaults to the authenticated user)"); pass into the data dict only when provided.
  • Client (src/client.py, create_time_entry): when user_id is present, set
    payload["_links"]["user"] = {"href": f"/api/v3/users/{data['user_id']}"}.
  • When user_id is omitted, behavior is unchanged. Mirror the optional-field pattern used for
    schedule_manually (tool field → data → client _links/payload). Keep the diff single-purpose.
  • Document the "Log time for other users" permission dependency in the tool docstring; the existing
    try/except → format_error surfaces permission errors.

Acceptance criteria

  • create_time_entry(..., user_id=<id>) creates an entry attributed to that user (verify via
    list_time_entries_embedded.user).
  • Omitting user_id logs under the authenticated user (unchanged).
  • A missing-permission / non-member case returns a clear error, not a crash.
  • Network-free unit test(s) (style of tests/test_update_readonly.py): _links.user set in the
    client payload when user_id given, absent when not.
  • uv run black . && flake8 . clean on changed lines; uv run pytest passes. README updated.

Out of scope

  • No unrelated reformatting. No change to the default log-as-authenticated-user behavior.
  • update_time_entry re-attribution and bulk backfill (possible follow-up).

Notes

  • Independent of PR-4 at the code level (adds a field to the wrapped CreateTimeEntryInput), but
    Cowork's JSON-string args need PR-4's CoercibleModel base — so for internal deployment it will be
    stacked on the fix branch; the single commit is cherry-pickable to main for a clean upstream PR.
  • Suggested branch feature/time-entry-user; commits cite Refs #N.

Type: enhancement. Label: backlog.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backlogLogged future work item, not yet scheduled

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions