Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions JobFlow.API/Controllers/AssignmentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,18 @@ public async Task<IActionResult> UpdateNotes(Guid id, [FromBody] UpdateAssignmen
return Ok(result.Value);
}

// Notify the client that the worker is en route
[HttpPost("{id:guid}/en-route")]
public async Task<IActionResult> NotifyEnRoute(Guid id)
{
var organizationId = HttpContext.GetOrganizationId();

var result = await _assignmentService.NotifyEnRouteAsync(organizationId, id);
if (result.IsFailure)
return BadRequest(result.Error);

return Ok(new { success = true });
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ NotificationMessage BuildClientJobRescheduled(

NotificationMessage BuildClientJobTrackingEta(OrganizationClient client, Job job, int etaMinutes);
NotificationMessage BuildClientJobTrackingArrival(OrganizationClient client, Job job);
NotificationMessage BuildClientJobTrackingEnRoute(OrganizationClient client, Job job);

NotificationMessage BuildEmployeeInvite(EmployeeInvite invite);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,21 @@ public NotificationMessage BuildClientJobTrackingArrival(OrganizationClient clie
};
}

public NotificationMessage BuildClientJobTrackingEnRoute(OrganizationClient client, Job job)
{
return new NotificationMessage
{
Email = client.EmailAddress,
Phone = client.PhoneNumber,
Name = client.FirstName,
Subject = $"Your worker is on the way for {job.Title}",
Body =
$"Hello {client.ClientFullName()},\n\nYour JobFlow worker is on the way for your job: {job.Title}.",
Sms = $"Your JobFlow worker is on the way for {job.Title}. ",
TemplateId = EmailTemplate.Default
};
}

public NotificationMessage BuildEmployeeInvite(EmployeeInvite invite)
{
var link = $"{baseUrl}/i/{invite.ShortCode}";
Expand Down
6 changes: 6 additions & 0 deletions JobFlow.Business/Notifications/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ public async Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClie
await SendNotificationAsync(message);
}

public async Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job)
{
var message = _builder.BuildClientJobTrackingEnRoute(client, job);
await SendNotificationAsync(message);
}

public async Task SendEmployeeInviteNotificationAsync(EmployeeInvite invite)
{
var message = _builder.BuildEmployeeInvite(invite);
Expand Down
30 changes: 30 additions & 0 deletions JobFlow.Business/Services/AssignmentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,26 @@ public async Task<Result<AssignmentDto>> GetAssignmentByIdAsync(
return Result.Success(await MapToDtoAsync(organizationId, assignment));
}

public async Task<Result> NotifyEnRouteAsync(Guid organizationId, Guid assignmentId)
{
var assignment = await _assignments.Query()
.Include(a => a.Job)
.ThenInclude(j => j.OrganizationClient)
.FirstOrDefaultAsync(a =>
a.Id == assignmentId &&
a.Job.OrganizationClient.OrganizationId == organizationId);

if (assignment == null)
return Result.Failure(AssignmentErrors.NotFound);

var job = assignment.Job;
if (job?.OrganizationClient == null)
return Result.Failure(Error.NotFound("Client.NotFound", "The client for this assignment could not be found."));

await _notificationService.SendClientJobTrackingEnRouteNotificationAsync(job.OrganizationClient, job);
return Result.Success();
}

private async Task<AssignmentDto> MapToDtoAsync(Guid organizationId, Assignment assignment)
{
var labelMapResult = await _workflowSettings.GetJobLifecycleLabelMapAsync(organizationId);
Expand Down Expand Up @@ -397,6 +417,16 @@ private AssignmentDto MapToDto(Assignment assignment, Dictionary<JobLifecycleSta
dto.ClientName = assignment.Job?.OrganizationClient != null
? $"{assignment.Job.OrganizationClient.FirstName} {assignment.Job.OrganizationClient.LastName}"
: null;

// Fall back to the client's address when the assignment-level override is empty.
var client = assignment.Job?.OrganizationClient;
if (client != null)
{
if (string.IsNullOrWhiteSpace(dto.Address1)) dto.Address1 = client.Address1;
if (string.IsNullOrWhiteSpace(dto.City)) dto.City = client.City;
if (string.IsNullOrWhiteSpace(dto.State)) dto.State = client.State;
if (string.IsNullOrWhiteSpace(dto.ZipCode)) dto.ZipCode = client.ZipCode;
}
dto.JobLifecycleStatus = assignment.Job?.LifecycleStatus ?? JobLifecycleStatus.Draft;
if (labelMap.TryGetValue(dto.JobLifecycleStatus, out var label))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ public interface IAssignmentService
Task<Result<List<AssignmentDto>>> GetAssignmentsAsync(Guid organizationId, DateTime start, DateTime end);

Task<Result<AssignmentDto>> GetAssignmentByIdAsync(Guid organizationId, Guid assignmentId);

Task<Result> NotifyEnRouteAsync(Guid organizationId, Guid assignmentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Task SendClientJobRescheduledNotificationAsync(
Task SendClientPaymentReceivedNotificationAsync(OrganizationClient client, Invoice invoice);
Task SendClientJobTrackingEtaNotificationAsync(OrganizationClient client, Job job, int etaMinutes);
Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClient client, Job job);
Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job);
Task SendClientEstimateSentNotificationAsync(OrganizationClient client, Estimate estimate);
Task SendClientEstimateFollowUpNotificationAsync(OrganizationClient client, Estimate estimate, string message);
Task SendOrganizationEstimateRevisionRequestedNotificationAsync(Organization organization, OrganizationClient client, Estimate estimate, string revisionMessage);
Expand Down
1 change: 1 addition & 0 deletions JobFlow.Tests/FollowUpAutomationServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ private sealed class NoOpNotificationService : INotificationService
public Task SendClientPaymentReceivedNotificationAsync(OrganizationClient client, Invoice invoice) => Task.CompletedTask;
public Task SendClientJobTrackingEtaNotificationAsync(OrganizationClient client, Job job, int etaMinutes) => Task.CompletedTask;
public Task SendClientJobTrackingArrivalNotificationAsync(OrganizationClient client, Job job) => Task.CompletedTask;
public Task SendClientJobTrackingEnRouteNotificationAsync(OrganizationClient client, Job job) => Task.CompletedTask;
public Task SendClientEstimateSentNotificationAsync(OrganizationClient client, Estimate estimate) => Task.CompletedTask;
public Task SendClientEstimateFollowUpNotificationAsync(OrganizationClient client, Estimate estimate, string message) => Task.CompletedTask;
public Task SendOrganizationEstimateRevisionRequestedNotificationAsync(Organization organization, OrganizationClient client, Estimate estimate, string revisionMessage) => Task.CompletedTask;
Expand Down
Loading