Skip to content

RE1-T115 Fixing dispatch settings issue#350

Merged
ucswift merged 2 commits intomasterfrom
develop
Apr 29, 2026
Merged

RE1-T115 Fixing dispatch settings issue#350
ucswift merged 2 commits intomasterfrom
develop

Conversation

@ucswift
Copy link
Copy Markdown
Member

@ucswift ucswift commented Apr 26, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added configurable dispatch and release status settings for units and personnel during call dispatch operations.
    • Automatic status application now applies configured statuses to units and groups when calls are dispatched or released.
  • Refactor

    • Centralized dispatch status management logic into a dedicated service layer for improved consistency across the application.

@request-info
Copy link
Copy Markdown

request-info Bot commented Apr 26, 2026

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 26, 2026

Warning

Rate limit exceeded

@ucswift has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 9 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 33e51a67-f933-4a22-9a36-a030eefe427b

📥 Commits

Reviewing files that changed from the base of the PR and between 272e27b and 6af0ea4.

⛔ Files ignored due to path filters (2)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx is excluded by !**/*.resx
📒 Files selected for processing (11)
  • Core/Resgrid.Model/DepartmentSettingTypes.cs
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs
  • Core/Resgrid.Model/UnitTypeCallStatusOverride.cs
  • Core/Resgrid.Services/CallDispatchStatusService.cs
  • Core/Resgrid.Services/DepartmentSettingsService.cs
  • Tests/Resgrid.Tests/Services/CallDispatchStatusServiceTests.cs
  • Tests/Resgrid.Tests/Services/DepartmentSettingsServiceDispatchStatusTests.cs
  • Web/Resgrid.Web.Services/Controllers/EmailController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs
  • Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
📝 Walkthrough

Walkthrough

This PR introduces a new service-oriented architecture for managing call dispatch and release statuses for both personnel (via shift signups) and units. A new ICallDispatchStatusService abstraction replaces inline controller logic, with settings configured through department settings. Multiple controllers and background workers are refactored to use this centralized service.

Changes

Cohort / File(s) Summary
Core Enum and Interface Definitions
Core/Resgrid.Model/DepartmentSettingTypes.cs, Core/Resgrid.Model/Services/ICallDispatchStatusService.cs, Core/Resgrid.Model/Services/IDepartmentSettingsService.cs
Extended DepartmentSettingTypes enum with two new values for unit call dispatch/release status configuration. Introduced new ICallDispatchStatusService interface with async methods to apply dispatch/release statuses to calls with optional group/unit filtering. Extended IDepartmentSettingsService with methods to retrieve configured dispatch/release status values.
Service Implementation
Core/Resgrid.Services/CallDispatchStatusService.cs, Core/Resgrid.Services/DepartmentSettingsService.cs, Core/Resgrid.Services/ServicesModule.cs
Implemented CallDispatchStatusService with shared ApplyStatusesAsync logic that resolves department settings, applies configured statuses to shift-enrolled personnel via action logs, and updates unit state records with call destination metadata. Extended DepartmentSettingsService to retrieve configured dispatch/release status integers. Registered new service in Autofac DI container.
Service Tests
Tests/Resgrid.Tests/Services/CallDispatchStatusServiceTests.cs, Tests/Resgrid.Tests/Services/DepartmentSettingsServiceDispatchStatusTests.cs
Added comprehensive test suite for CallDispatchStatusService validating dispatch/release status application for shift personnel and units, including fallback behavior when auto status is disabled. Added tests for DepartmentSettingsService dispatch/release status retrieval with missing/present setting scenarios.
Controller Integration
Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs, Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs
Refactored to inject and delegate call dispatch/release status application to ICallDispatchStatusService instead of computing shift signups and setting action logs inline. Added dispatch-status diffing to detect cancelled/added group/unit assignments and conditionally apply release/dispatch statuses.
Department Settings UI
Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs, Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs, Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
Extended DispatchSettingsView model with unit status selection list and integer fields for unit dispatch/release status configuration. Refactored settings population into shared helpers and added UI controls for unit status level selection with localized labels. Updated POST handler to persist new unit status settings.
Worker Integration
Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs, Workers/Resgrid.Workers.Framework/Logic/CallBroadcast.cs
Updated DispatchScheduledCallsTask to resolve ICallDispatchStatusService and apply dispatch statuses after successful call broadcast. Enhanced CallBroadcast to dispatch individually to each shift signup user when shift-based dispatch is enabled, preventing duplicate sends via dispatchedUsers tracking.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant CallDispatchStatusService
    participant DepartmentSettingsService
    participant Repository
    participant UserActionLogService
    participant UnitStateService

    Client->>CallDispatchStatusService: ApplyDispatchStatusesAsync(call, groupIds, unitIds)
    
    CallDispatchStatusService->>DepartmentSettingsService: GetUnitCallDispatchStatusToSetAsync(departmentId)
    DepartmentSettingsService->>Repository: Query department setting
    Repository-->>DepartmentSettingsService: Setting value or null
    DepartmentSettingsService-->>CallDispatchStatusService: status (int)
    
    alt Group IDs provided and shift dispatch enabled
        CallDispatchStatusService->>Repository: Get shift signups for shift date
        Repository-->>CallDispatchStatusService: User IDs from signups
        CallDispatchStatusService->>UserActionLogService: Set action log status for each user
        UserActionLogService->>Repository: Persist action logs
    end
    
    alt Unit IDs provided
        CallDispatchStatusService->>UnitStateService: Set unit state for each unit
        UnitStateService->>Repository: Persist unit states with call destination metadata
    end
    
    CallDispatchStatusService-->>Client: Task complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Develop #234: Modifies DepartmentSettingTypes enum by adding new setting members; shares the same enum extension pattern used in this PR.
  • RE1-T107 Added checkin timers to calls #312: Extends DepartmentSettingTypes enum with different configuration keys; related through shared enum modification approach but addresses distinct features.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'RE1-T115 Fixing dispatch settings issue' is vague and doesn't clearly convey what dispatch settings issue is being fixed or what the main change accomplishes. Replace with a more specific title that describes the actual change, such as 'Add dispatch status configuration for units and personnel' or 'Implement configurable dispatch status management service'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 21 minutes and 9 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
Workers/Resgrid.Workers.Framework/Logic/CallBroadcast.cs (1)

119-141: Optional refactor: extract a single "dispatch-once" helper.

This signup loop is the third near-identical copy of the same shape (see lines 145–164 for groups, 192–214 for unit-assigned personnel, 223–242 for unit-station-group members, 258–277 for roles). Pulling the dedup + profile lookup + try/catch into a small local function would remove duplication and make it harder to drift error handling between branches.

♻️ Suggested helper
async Task DispatchUserOnceAsync(string userId)
{
    if (string.IsNullOrEmpty(userId) || !dispatchedUsers.Add(userId))
        return;

    try
    {
        var profile = cqi.Profiles.FirstOrDefault(x => x.UserId == userId);
        await _communicationService.SendCallAsync(
            cqi.Call,
            new CallDispatch { UserId = userId },
            cqi.DepartmentTextNumber,
            cqi.Call.DepartmentId,
            profile,
            cqi.Address);
    }
    catch (SocketException) { /* swallow, matches existing behavior */ }
    catch (Exception ex)    { Logging.LogException(ex); }
}

Then the signup branch collapses to:

-						foreach (var signup in signups)
-						{
-							if (!dispatchedUsers.Contains(signup.UserId))
-							{
-								dispatchedUsers.Add(signup.UserId);
-								try
-								{
-									var profile = cqi.Profiles.FirstOrDefault(x => x.UserId == signup.UserId);
-									await _communicationService.SendCallAsync(cqi.Call, new CallDispatch() { UserId = signup.UserId }, cqi.DepartmentTextNumber, cqi.Call.DepartmentId, profile, cqi.Address);
-								}
-								catch (SocketException sex)
-								{
-								}
-								catch (Exception ex)
-								{
-									Logging.LogException(ex);
-								}
-							}
-						}
+						foreach (var signup in signups)
+							await DispatchUserOnceAsync(signup.UserId);

As per coding guidelines: "Use extension methods appropriately for domain-specific operations" and prefer "composition with interfaces to extend behavior" — extracting the per-user dispatch reduces duplication and isolates the side effect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Workers/Resgrid.Workers.Framework/Logic/CallBroadcast.cs` around lines 119 -
141, Extract the repeated "dispatch-once" logic into a local helper (e.g.,
DispatchUserOnceAsync) inside CallBroadcast so each branch calls it instead of
duplicating code: the helper should accept a userId, check for null/empty and
use dispatchedUsers.Add(userId) to ensure single dispatch, perform the profile
lookup via cqi.Profiles.FirstOrDefault(x => x.UserId == userId), call
_communicationService.SendCallAsync(cqi.Call, new CallDispatch { UserId = userId
}, cqi.DepartmentTextNumber, cqi.Call.DepartmentId, profile, cqi.Address), and
preserve existing exception handling (swallow SocketException, log other
exceptions via Logging.LogException); replace the foreach blocks that currently
contain the dedupe/lookup/try-catch with calls to
DispatchUserOnceAsync(signup.UserId) (and analogous places for groups/roles/unit
members).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Core/Resgrid.Services/CallDispatchStatusService.cs`:
- Around line 145-165: GetShiftDate/GetReferenceDate in
CallDispatchStatusService currently always uses the call's stored timestamps
(LastDispatchedOn/DispatchOn/LoggedOn) which can be stale when UpdateCall
performs incremental group additions; modify the service to accept an optional
effective operation time (DateTime? effectiveTime) and use that as the primary
reference in GetReferenceDate (fall back to LastDispatchedOn/DispatchOn/LoggedOn
and then DateTime.UtcNow if effectiveTime is null), update GetShiftDate to use
the new GetReferenceDate signature, and change callers (e.g.,
DispatchController.UpdateCall) to pass the current operation time when doing
incremental updates so shift resolution uses the operation time instead of the
old dispatch timestamp.

---

Nitpick comments:
In `@Workers/Resgrid.Workers.Framework/Logic/CallBroadcast.cs`:
- Around line 119-141: Extract the repeated "dispatch-once" logic into a local
helper (e.g., DispatchUserOnceAsync) inside CallBroadcast so each branch calls
it instead of duplicating code: the helper should accept a userId, check for
null/empty and use dispatchedUsers.Add(userId) to ensure single dispatch,
perform the profile lookup via cqi.Profiles.FirstOrDefault(x => x.UserId ==
userId), call _communicationService.SendCallAsync(cqi.Call, new CallDispatch {
UserId = userId }, cqi.DepartmentTextNumber, cqi.Call.DepartmentId, profile,
cqi.Address), and preserve existing exception handling (swallow SocketException,
log other exceptions via Logging.LogException); replace the foreach blocks that
currently contain the dedupe/lookup/try-catch with calls to
DispatchUserOnceAsync(signup.UserId) (and analogous places for groups/roles/unit
members).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c5aba14e-e1d7-4104-87c3-0732bc426add

📥 Commits

Reviewing files that changed from the base of the PR and between 22d531b and 272e27b.

⛔ Files ignored due to path filters (2)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx is excluded by !**/*.resx
📒 Files selected for processing (15)
  • Core/Resgrid.Model/DepartmentSettingTypes.cs
  • Core/Resgrid.Model/Services/ICallDispatchStatusService.cs
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs
  • Core/Resgrid.Services/CallDispatchStatusService.cs
  • Core/Resgrid.Services/DepartmentSettingsService.cs
  • Core/Resgrid.Services/ServicesModule.cs
  • Tests/Resgrid.Tests/Services/CallDispatchStatusServiceTests.cs
  • Tests/Resgrid.Tests/Services/DepartmentSettingsServiceDispatchStatusTests.cs
  • Web/Resgrid.Web.Services/Controllers/v4/CallsController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DispatchController.cs
  • Web/Resgrid.Web/Areas/User/Models/Departments/DispatchSettingsView.cs
  • Web/Resgrid.Web/Areas/User/Views/Department/DispatchSettings.cshtml
  • Workers/Resgrid.Workers.Console/Tasks/DispatchScheduledCallsTask.cs
  • Workers/Resgrid.Workers.Framework/Logic/CallBroadcast.cs

Comment thread Core/Resgrid.Services/CallDispatchStatusService.cs
Comment on lines +145 to +165
private static DateTime GetShiftDate(Call call, Department department)
{
var referenceDate = GetReferenceDate(call);
var localizedDate = department != null ? TimeConverterHelper.TimeConverter(referenceDate, department) : referenceDate;

return new DateTime(localizedDate.Year, localizedDate.Month, localizedDate.Day);
}

private static DateTime GetReferenceDate(Call call)
{
if (call.LastDispatchedOn.HasValue)
return call.LastDispatchedOn.Value;

if (call.DispatchOn.HasValue)
return call.DispatchOn.Value;

if (call.LoggedOn != default(DateTime))
return call.LoggedOn;

return DateTime.UtcNow;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Shift resolution uses a stale date for later dispatch changes.

For calls that were already dispatched earlier, GetReferenceDate locks shift lookups to LastDispatchedOn/DispatchOn. DispatchController.UpdateCall now reuses this service when new groups are added later, so a group added after a shift/day boundary can resolve yesterday’s signups instead of the crew currently being dispatched. Pass the effective operation time into the service, or fall back to DateTime.UtcNow for incremental update flows.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Core/Resgrid.Services/CallDispatchStatusService.cs` around lines 145 - 165,
GetShiftDate/GetReferenceDate in CallDispatchStatusService currently always uses
the call's stored timestamps (LastDispatchedOn/DispatchOn/LoggedOn) which can be
stale when UpdateCall performs incremental group additions; modify the service
to accept an optional effective operation time (DateTime? effectiveTime) and use
that as the primary reference in GetReferenceDate (fall back to
LastDispatchedOn/DispatchOn/LoggedOn and then DateTime.UtcNow if effectiveTime
is null), update GetShiftDate to use the new GetReferenceDate signature, and
change callers (e.g., DispatchController.UpdateCall) to pass the current
operation time when doing incremental updates so shift resolution uses the
operation time instead of the old dispatch timestamp.

Comment on lines +883 to +891
var shouldApplyDispatchStatuses = call.HasBeenDispatched.GetValueOrDefault() || !call.DispatchOn.HasValue || call.DispatchOn.Value <= DateTime.UtcNow;
if (shouldApplyDispatchStatuses && (cancelledGroupIds.Any() || cancelledUnitIds.Any()))
{
await _callDispatchStatusService.ApplyReleaseStatusesAsync(call, cancelledGroupIds, cancelledUnitIds, cancellationToken);
}

if (shouldApplyDispatchStatuses && (newGroupIds.Any() || newUnitIds.Any()))
{
await _callDispatchStatusService.ApplyDispatchStatusesAsync(call, newGroupIds, newUnitIds, cancellationToken);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid clearing responders who are still dispatched through another shift group.

This releases everyone resolved from cancelledGroupIds in isolation. If multi-group shift signups are allowed, a responder who belongs to both a removed group and a still-dispatched group will still get the clear status here. Filter out users who remain covered by retained/new group dispatches before calling the release path.

@ucswift
Copy link
Copy Markdown
Member Author

ucswift commented Apr 29, 2026

Approve

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift ucswift merged commit af8b2c9 into master Apr 29, 2026
18 of 19 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.

1 participant