Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8c13f66
fix(opgaver-grpc): update PlanningCaseSite + PlanningCase on completion
renemadsen May 6, 2026
5edb564
fixup(opgaver-grpc): lookup PlanningCaseSite by MicrotingSdkCaseId
renemadsen May 6, 2026
3f20b89
fix(opgaver-grpc): treat NULL WorkflowState as not-removed in queries
renemadsen May 6, 2026
46ebfcc
fix(opgaver-grpc): SetFieldValue — resolve FieldValue.Id from (caseId…
renemadsen May 6, 2026
b1b93fe
fix(opgaver-grpc): filter compliance lookup by current worker's site
renemadsen May 6, 2026
dda33db
feat(opgaver-grpc): consume client-echoed compliance_id + sdk_case_id
renemadsen May 6, 2026
c263780
feat(opgaver): filter non-actionable opgaver from calendar emit
renemadsen May 7, 2026
3802ece
refactor(opgaver): scope actionable-only filter to mobile worker calls
renemadsen May 7, 2026
7703eb5
fix(opgaver): make CompleteOpgave idempotent for Completed=false
renemadsen May 7, 2026
e885cb6
fix(opgaver): emit per-case FieldValue, surface CaseUpdate failures
renemadsen May 7, 2026
47f2065
feat(opgaver): new ListTaskTracker RPC mirroring angular task-tracker
renemadsen May 7, 2026
e177973
fix(opgaver): mirror angular case-closure in CompleteOpgave
renemadsen May 7, 2026
db7dc6c
fix(opgaver): mirror angular case soft-delete after CompleteOpgave
renemadsen May 7, 2026
93b1184
fix(opgaver): canonicalize CheckBox + select values in SetFieldValue
renemadsen May 7, 2026
ddd909d
fix(opgaver): write empty-string to NULL FieldValues on CompleteOpgave
renemadsen May 7, 2026
45b0dc4
Revert "fix(opgaver): write empty-string to NULL FieldValues on Compl…
renemadsen May 7, 2026
98c782e
fix(opgaver): mirror angular GET-case FieldValue selector on Complete
renemadsen May 7, 2026
1ba2f8c
fix(opgaver): mirror angular FieldValue write + extension format on U…
renemadsen May 7, 2026
1cd0113
fix(opgaver): propagate compliance ids on retracted recurrences + fal…
renemadsen May 8, 2026
3a4c4db
feat(opgaver): bundle field-value + comment writes onto CompleteOpgave
renemadsen May 8, 2026
478df18
fix(opgaver): materialize FieldValues via CaseRead before bundle apply
renemadsen May 8, 2026
90a250a
fix(opgaver): DoneAt = compliance.Deadline (not DateTime.UtcNow)
renemadsen May 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,18 @@ public class CalendarTaskRequestModel
public List<int> BoardIds { get; set; } = [];
public List<string> TagNames { get; set; } = [];
public List<int> SiteIds { get; set; } = [];

/// <summary>
/// When true, the calendar emits only *actionable* compliance rows for the requested
/// week — i.e. compliances whose backing SDK Case still exists, is not soft-deleted,
/// and is not yet completed (Status != 100). This is intended for the mobile-worker
/// gRPC path (<c>OpgaverGrpcService</c>) where non-actionable rows have no write
/// handler to bind to and would just clutter the worker's view.
///
/// Default <c>false</c> preserves the historical behavior used by the angular admin
/// calendar (<c>CalendarController</c>) and other gRPC consumers
/// (<c>CalendarGrpcService</c>): all in-week compliances surface, including missed
/// and completed ones, so the admin can audit the full week.
/// </summary>
public bool ActionableOnly { get; set; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,12 @@ public class CalendarTaskResponseModel
public int? ItemPlanningTagId { get; set; }
public string? DescriptionHtml { get; set; }
public List<CalendarTaskAttachmentDto> Attachments { get; set; } = new();

/// <summary>
/// True when the underlying compliance/case is past deadline AND not
/// completed (or retracted with SDK Case Status=77). Populated by the
/// task-tracker service path; the calendar-week path leaves this as
/// false (it pre-filters non-actionable rows out).
/// </summary>
public bool TaskIsExpired { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ service Opgaver {
rpc ListEjendomme(ListEjendommeRequest) returns (ListEjendommeResponse);
rpc ListTavler(ListTavlerRequest) returns (ListTavlerResponse);
rpc ListOpgaver(ListOpgaverRequest) returns (ListOpgaverResponse);
rpc ListTaskTracker(ListTaskTrackerRequest) returns (ListTaskTrackerResponse);
rpc StreamOpgaveChanges(StreamOpgaveChangesRequest) returns (stream OpgaveChange);
rpc CompleteOpgave(CompleteOpgaveRequest) returns (CompleteOpgaveResponse);
rpc SetComment(SetCommentRequest) returns (SetCommentResponse);
Expand All @@ -30,6 +31,13 @@ message UploadPhotoMeta {
int32 slot = 2;
string content_type = 3;
int64 client_ts_unix = 4;
// Server-assigned PK of the Compliance row this opgave was emitted from
// (see Index handler). 0 means "unknown" — legacy clients that pre-date
// this field; server falls back to the site-filtered fuzzy lookup in
// that case so in-flight outbox payloads keep draining.
int64 compliance_id = 5;
// Server-assigned PK of the SDK Case row backing this opgave. 0 = legacy.
int64 microting_sdk_case_id = 6;
}
message UploadPhotoResponse {
string storage_id = 1;
Expand All @@ -39,6 +47,9 @@ message RemovePhotoRequest {
string opgave_id = 1;
int32 slot = 2;
int64 client_ts_unix = 3;
// See UploadPhotoMeta.compliance_id / microting_sdk_case_id. 0 = legacy.
int64 compliance_id = 4;
int64 microting_sdk_case_id = 5;
}
message RemovePhotoResponse {}

Expand All @@ -47,13 +58,43 @@ message CompleteOpgaveRequest {
bool completed = 2;
string completed_by = 3;
int64 client_ts_unix = 4;
// See UploadPhotoMeta.compliance_id / microting_sdk_case_id. 0 = legacy.
int64 compliance_id = 5;
int64 microting_sdk_case_id = 6;
// Bundled field-value writes applied AFTER the case is revived
// (WorkflowState='created') and BEFORE the closure cascade
// (PlanningCase/PlanningCaseSite Status=100 + core.CaseDelete soft-delete).
// Empty for legacy clients — server still completes the case the same way.
// Each entry corresponds to one prior SetFieldValue RPC; the bundle
// collapses worker edits into a single round-trip at Complete time.
repeated FieldValueWrite field_values = 7;
// Optional comment write applied alongside field_values, same lifecycle
// window. Empty string ("") means "no comment change" — the existing
// Cases.Custom envelope is left untouched. Non-empty replaces the
// OpgaverComment body verbatim, matching SetComment semantics.
string comment = 8;
}
message CompleteOpgaveResponse { Opgave opgave = 1; }

// One field-value write bundled into CompleteOpgaveRequest.field_values.
// Mirrors the per-RPC SetFieldValueRequest pair (field_id + value) so the
// server-side handler can reuse the same lookup + canonicalization helpers.
message FieldValueWrite {
// SDK Field.Id — matches SetFieldValueRequest.field_id and FormField.id.
// Server rejects field_id <= 0.
int32 field_id = 1;
// Raw worker-facing value; canonicalized server-side per the existing
// CanonicalizeFieldValueAsync helper (CheckBox + Select normalization).
string value = 2;
}

message SetCommentRequest {
string opgave_id = 1;
string text = 2;
int64 client_ts_unix = 3;
// See UploadPhotoMeta.compliance_id / microting_sdk_case_id. 0 = legacy.
int64 compliance_id = 4;
int64 microting_sdk_case_id = 5;
}
message SetCommentResponse { Opgave opgave = 1; }

Expand All @@ -62,6 +103,9 @@ message SetFieldValueRequest {
int32 field_id = 2;
string value = 3;
int64 client_ts_unix = 4;
// See UploadPhotoMeta.compliance_id / microting_sdk_case_id. 0 = legacy.
int64 compliance_id = 5;
int64 microting_sdk_case_id = 6;
}
message SetFieldValueResponse { Opgave opgave = 1; }

Expand All @@ -79,6 +123,16 @@ message ListOpgaverRequest {
}
message ListOpgaverResponse { repeated Opgave opgaver = 1; }

// Full property-scoped compliance list for the mobile worker's "task
// tracker" view (mirror of the angular admin's BackendConfigurationTaskTrackerHelper.Index).
// No deadline window — actionable + missed + completed rotations are all
// returned, with per-row status carried on the existing `completed` field
// and the new `task_is_expired` field (see Opgave below).
message ListTaskTrackerRequest {
int32 property_id = 1;
}
message ListTaskTrackerResponse { repeated Opgave opgaver = 1; }

message StreamOpgaveChangesRequest {
string ejendom_id = 1;
string tavle_id = 2;
Expand Down Expand Up @@ -122,6 +176,23 @@ message Opgave {
// Form-field structure + current values for the eForm template backing
// this opgave. Empty when no Case exists yet (recurrence-only tasks).
repeated FormField fields = 15;
// Server-assigned PK of the Compliance row this opgave was emitted from.
// Round-tripped through the Flutter Drift cache and back on every write
// so the server can resolve compliance + sdk case deterministically
// (no fuzzy OrderBy(Deadline).First() lookup). 0 = recurrence-only task
// with no backing compliance yet (or legacy server pre-dating this field).
int64 compliance_id = 16;
// Server-assigned PK of the SDK Case row backing this opgave. 0 means
// "no backing case" (recurrence-only) or a legacy server.
int64 microting_sdk_case_id = 17;
// True when the opgave's deadline has passed AND the underlying case is
// either retracted (Case.WorkflowState=Removed AND Status=77) or simply
// past its deadline without being completed (Case.Deadline < UtcNow AND
// Status != 100). Surfaced primarily by ListTaskTracker so flutter can
// render missed rotations in red. The calendar-week view (ListOpgaver)
// pre-filters non-actionable rows out and so emits this as false; default
// proto3 zero-value remains backwards-compatible with older clients.
bool task_is_expired = 18;
}

message Attachment {
Expand Down
Loading