diff --git a/JobFlow.API/Controllers/InventoryController.cs b/JobFlow.API/Controllers/InventoryController.cs index 419e732..9acede0 100644 --- a/JobFlow.API/Controllers/InventoryController.cs +++ b/JobFlow.API/Controllers/InventoryController.cs @@ -1,4 +1,5 @@ -using JobFlow.Business.Extensions; +using JobFlow.API.Extensions; +using JobFlow.Business.Extensions; using JobFlow.Business.Services.ServiceInterfaces; using JobFlow.Domain.Models; using Microsoft.AspNetCore.Mvc; @@ -19,13 +20,21 @@ public InventoryController(IInventoryService service) [HttpGet("{id}")] public async Task Get(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); var result = await _service.GetByIdAsync(id); - return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblemDetails(); + if (!result.IsSuccess) + return result.ToProblemDetails(); + if (result.Value.OrganizationId != organizationId) + return Results.NotFound(); + return Results.Ok(result.Value); } [HttpGet("org/{orgId}")] public async Task GetAll(Guid orgId) { + var organizationId = HttpContext.GetOrganizationId(); + if (orgId != organizationId) + return Results.Forbid(); var result = await _service.GetAllAsync(orgId); return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblemDetails(); } @@ -33,6 +42,8 @@ public async Task GetAll(Guid orgId) [HttpPost] public async Task Create([FromBody] InventoryItem item) { + var organizationId = HttpContext.GetOrganizationId(); + item.OrganizationId = organizationId; var result = await _service.CreateAsync(item); return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblemDetails(); } @@ -40,6 +51,8 @@ public async Task Create([FromBody] InventoryItem item) [HttpPut] public async Task Update([FromBody] InventoryItem item) { + var organizationId = HttpContext.GetOrganizationId(); + item.OrganizationId = organizationId; var result = await _service.UpdateAsync(item); return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblemDetails(); } @@ -47,6 +60,12 @@ public async Task Update([FromBody] InventoryItem item) [HttpDelete("{id}")] public async Task Delete(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); + var existing = await _service.GetByIdAsync(id); + if (!existing.IsSuccess) + return existing.ToProblemDetails(); + if (existing.Value.OrganizationId != organizationId) + return Results.NotFound(); var result = await _service.DeleteAsync(id); return result.IsSuccess ? Results.Ok() : result.ToProblemDetails(); } diff --git a/JobFlow.API/Controllers/InvoiceComtroller.cs b/JobFlow.API/Controllers/InvoiceComtroller.cs index 1063dae..4530ab1 100644 --- a/JobFlow.API/Controllers/InvoiceComtroller.cs +++ b/JobFlow.API/Controllers/InvoiceComtroller.cs @@ -46,15 +46,38 @@ IMapper mapper public async Task Get(Guid id) { var result = await invoiceService.GetInvoiceByIdAsync(id); + if (!result.IsSuccess) + return NotFound(result.Error); + + var organizationId = HttpContext.GetOrganizationId(); + if (result.Value.OrganizationId != organizationId) + return NotFound(); + var value = _mapper.Map(result.Value); - return result.IsSuccess ? Ok(value) : NotFound(result.Error); + return Ok(value); + } + + [HttpGet("organization/summary")] + public async Task GetSummary() + { + var organizationId = HttpContext.GetOrganizationId(); + if (organizationId == Guid.Empty) + return Unauthorized("Organization context missing."); + + var result = await invoiceService.GetInvoiceAggregatesByOrganizationAsync(organizationId); + return result.IsSuccess ? Ok(result.Value) : BadRequest(result.Error); } [HttpGet("client/{clientId}")] public async Task GetByClient(Guid clientId) { + var organizationId = HttpContext.GetOrganizationId(); var result = await invoiceService.GetInvoicesByClientAsync(clientId); - return Ok(result.Value.ToDto()); + if (!result.IsSuccess) + return NotFound(result.Error); + + var filtered = result.Value.Where(i => i.OrganizationId == organizationId).ToList(); + return Ok(filtered.ToDto()); } [HttpGet("organization")] @@ -108,6 +131,8 @@ public async Task Upsert( var invoiceNumber = await numberGenerator.GenerateAsync(organizationId); var jobInfo = await this._jobService.GetJobByIdAsync(request.JobId, organizationId); + if (!jobInfo.IsSuccess) + return BadRequest(jobInfo.Error); request.OrganizationClientId = jobInfo.Value.OrganizationClientId; var invoice = request.ToInvoice(invoiceNumber); @@ -140,9 +165,12 @@ public Task UpsertForOrganization([FromBody] CreateInvoiceRequest [HttpPost("{id:guid}/send")] public async Task SendInvoice(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); var result = await invoiceService.GetInvoiceByIdAsync(id); if (!result.IsSuccess) return NotFound(result.Error); + if (result.Value.OrganizationId != organizationId) + return NotFound(); await invoiceService.SendInvoiceToClientAsync(result.Value.Id); @@ -152,14 +180,14 @@ public async Task SendInvoice(Guid id) [HttpPost("{id:guid}/remind")] public async Task SendInvoiceReminder(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); var result = await invoiceService.GetInvoiceByIdAsync(id); if (!result.IsSuccess) return NotFound(result.Error); var invoice = result.Value; - - if (invoice.OrganizationClient is null) - return BadRequest("Invoice client is missing."); + if (invoice.OrganizationId != organizationId) + return NotFound(); if (invoice.OrganizationClient is null) return BadRequest("Invoice client is missing."); @@ -196,7 +224,13 @@ await notificationService.SendClientInvoiceReminderNotificationAsync( [HttpDelete("{id:guid}")] public async Task Delete(Guid id) { - // Optional: delete line items too + var organizationId = HttpContext.GetOrganizationId(); + var existing = await invoiceService.GetInvoiceByIdAsync(id); + if (!existing.IsSuccess) + return NotFound(existing.Error); + if (existing.Value.OrganizationId != organizationId) + return NotFound(); + await lineItemService.DeleteByInvoiceIdAsync(id); var result = await invoiceService.DeleteInvoiceAsync(id); return result.IsSuccess ? Ok() : NotFound(result.Error); @@ -205,13 +239,15 @@ public async Task Delete(Guid id) [HttpGet("{id:guid}/pdf")] public async Task GeneratePdf(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); var result = await invoiceService.GetInvoiceByIdAsync(id); if (!result.IsSuccess) return NotFound(result.Error); + if (result.Value.OrganizationId != organizationId) + return NotFound(); //46455c4d-58c0-49ef-b18a-84704dbd50aa var pdf = await pdfGenerator.GenerateInvoicePdfAsync(result.Value); var invoice = result.Value; - await invoiceService.SendInvoiceToClientAsync(invoice.Id); var pdfName = $"{invoice.OrganizationClient.Organization.OrganizationName}-Invoice-{invoice.InvoiceNumber}.pdf"; return File(pdf, "application/pdf", $"{pdfName}"); } diff --git a/JobFlow.API/Controllers/JobController.cs b/JobFlow.API/Controllers/JobController.cs index 0f9b590..08425af 100644 --- a/JobFlow.API/Controllers/JobController.cs +++ b/JobFlow.API/Controllers/JobController.cs @@ -88,7 +88,8 @@ public async Task UpsertJob([FromBody] JobDto model) [HttpDelete("{id:guid}")] public async Task DeleteJob(Guid id) { - var result = await _jobService.DeleteJobAsync(id); + var organizationId = HttpContext.GetOrganizationId(); + var result = await _jobService.DeleteJobAsync(id, organizationId); if (result.IsFailure) return BadRequest(result.Error); diff --git a/JobFlow.API/Controllers/PaymentController.cs b/JobFlow.API/Controllers/PaymentController.cs index f193979..68a75ad 100644 --- a/JobFlow.API/Controllers/PaymentController.cs +++ b/JobFlow.API/Controllers/PaymentController.cs @@ -458,6 +458,11 @@ public async Task RefundPayment([FromBody] PaymentRefundRequestDt : null }); + if (result.Success) + { + await _invoiceService.RecordRefundAsync(request.InvoiceId.Value, request.Amount); + } + return result.Success ? Ok(result) : BadRequest(result); } diff --git a/JobFlow.API/Controllers/PriceBookItemController.cs b/JobFlow.API/Controllers/PriceBookItemController.cs index 8c054fe..a49506c 100644 --- a/JobFlow.API/Controllers/PriceBookItemController.cs +++ b/JobFlow.API/Controllers/PriceBookItemController.cs @@ -21,8 +21,13 @@ public PriceBookController(IPriceBookItemService service) [HttpGet("{id}")] public async Task Get(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); var result = await _service.GetByIdAsync(id); - return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblemDetails(); + if (!result.IsSuccess) + return result.ToProblemDetails(); + if (result.Value.OrganizationId != organizationId) + return Results.NotFound(); + return Results.Ok(result.Value); } [HttpGet("category/{categoryId}")] @@ -75,6 +80,8 @@ public async Task Update([FromBody] UpdatePriceBookItemRequest dto) Description = dto.Description, Unit = dto.Unit, PricePerUnit = dto.Cost, + Price = dto.Price, + Cost = dto.Cost, ItemType = dto.Type, InventoryUnitsPerSale = dto.InventoryUnitsPerSale, CategoryId = dto.CategoryId @@ -86,6 +93,12 @@ public async Task Update([FromBody] UpdatePriceBookItemRequest dto) [HttpDelete("{id}")] public async Task Delete(Guid id) { + var organizationId = HttpContext.GetOrganizationId(); + var existing = await _service.GetByIdAsync(id); + if (!existing.IsSuccess) + return existing.ToProblemDetails(); + if (existing.Value.OrganizationId != organizationId) + return Results.NotFound(); var result = await _service.DeleteAsync(id); return result.IsSuccess ? Results.Ok() : result.ToProblemDetails(); } diff --git a/JobFlow.API/Models/CreateInvoiceRequest.cs b/JobFlow.API/Models/CreateInvoiceRequest.cs index 97a0e13..699ba4b 100644 --- a/JobFlow.API/Models/CreateInvoiceRequest.cs +++ b/JobFlow.API/Models/CreateInvoiceRequest.cs @@ -13,7 +13,7 @@ public class InvoiceLineItemDto { public Guid? PriceBookItemId { get; set; } public string Description { get; set; } = null!; - public int Quantity { get; set; } + public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal LineTotal { get; set; } } \ No newline at end of file diff --git a/JobFlow.Business/ModelErrors/EstimateErrors.cs b/JobFlow.Business/ModelErrors/EstimateErrors.cs index c41a284..37aaa35 100644 --- a/JobFlow.Business/ModelErrors/EstimateErrors.cs +++ b/JobFlow.Business/ModelErrors/EstimateErrors.cs @@ -19,4 +19,7 @@ public static class EstimateErrors public static readonly Error CannotRespondInCurrentStatus = Error.Conflict("Estimate.CannotRespondInCurrentStatus", "Only sent estimates can be accepted or declined."); + + public static readonly Error CannotEditInCurrentStatus = + Error.Conflict("Estimate.CannotEditInCurrentStatus", "Estimates that have been sent, accepted, or declined cannot be edited."); } \ No newline at end of file diff --git a/JobFlow.Business/ModelErrors/InvoiceErrors.cs b/JobFlow.Business/ModelErrors/InvoiceErrors.cs index bbcd619..ff995b5 100644 --- a/JobFlow.Business/ModelErrors/InvoiceErrors.cs +++ b/JobFlow.Business/ModelErrors/InvoiceErrors.cs @@ -3,4 +3,6 @@ public static class InvoiceErrors { public static readonly Error NotFound = Error.NotFound("Invoice.NotFound", "The invoice was not found."); + public static readonly Error InvalidAmount = Error.Validation("Invoice.InvalidAmount", "The refund amount must be greater than zero."); + public static readonly Error RefundExceedsTotal = Error.Validation("Invoice.RefundExceedsTotal", "The total refunded amount cannot exceed the invoice total."); } \ No newline at end of file diff --git a/JobFlow.Business/Models/DTOs/InvoiceAggregateDto.cs b/JobFlow.Business/Models/DTOs/InvoiceAggregateDto.cs index a4355a2..693f72d 100644 --- a/JobFlow.Business/Models/DTOs/InvoiceAggregateDto.cs +++ b/JobFlow.Business/Models/DTOs/InvoiceAggregateDto.cs @@ -2,6 +2,13 @@ namespace JobFlow.Business.Models.DTOs; public class InvoiceAggregateDto { - public decimal Outstanding { get; set; } public int InvoiceCount { get; set; } + public int DraftCount { get; set; } + public int SentCount { get; set; } + public int PaidCount { get; set; } + public int OverdueCount { get; set; } + public int RefundedCount { get; set; } + public decimal TotalBilled { get; set; } + public decimal BalanceDue { get; set; } + public decimal Outstanding { get; set; } } diff --git a/JobFlow.Business/Services/EstimateNumberGenerator.cs b/JobFlow.Business/Services/EstimateNumberGenerator.cs new file mode 100644 index 0000000..e4d9891 --- /dev/null +++ b/JobFlow.Business/Services/EstimateNumberGenerator.cs @@ -0,0 +1,77 @@ +using System.Data; +using JobFlow.Business.DI; +using JobFlow.Business.Services.ServiceInterfaces; +using JobFlow.Domain; +using JobFlow.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace JobFlow.Business.Services; + +[ScopedService] +public class EstimateNumberGenerator : IEstimateNumberGenerator +{ + private readonly ILogger _logger; + private readonly IRepository _sequenceRepo; + private readonly IUnitOfWork _unitOfWork; + + public EstimateNumberGenerator( + IUnitOfWork unitOfWork, + ILogger logger) + { + _unitOfWork = unitOfWork; + _sequenceRepo = _unitOfWork.RepositoryOf(); + _logger = logger; + } + + public async Task GenerateAsync(Guid organizationId) + { + var now = DateTime.UtcNow; + var year = now.Year; + var month = now.Month; + var day = now.Day; + var estimateNumber = string.Empty; + + var dbContext = _unitOfWork.Context; + var strategy = dbContext.Database.CreateExecutionStrategy(); + + await strategy.ExecuteAsync(async () => + { + using var transaction = await dbContext.Database.BeginTransactionAsync(IsolationLevel.Serializable); + + var sequence = await _sequenceRepo.Query() + .SingleOrDefaultAsync(s => + s.OrganizationId == organizationId && + s.Year == year && + s.Month == month && + s.Day == day); + + if (sequence == null) + { + sequence = new EstimateSequence + { + OrganizationId = organizationId, + Year = year, + Month = month, + Day = day, + LastSequence = 0 + }; + await _sequenceRepo.AddAsync(sequence); + } + + sequence.LastSequence++; + var nextSeq = sequence.LastSequence; + + await _unitOfWork.SaveChangesAsync(); + await transaction.CommitAsync(); + + estimateNumber = $"EST-{year}{month:D2}{day:D2}-{nextSeq:D4}"; + }); + + _logger.LogInformation( + "Generated estimate number {EstimateNumber} for organization {OrgId}", + estimateNumber, organizationId); + + return estimateNumber; + } +} diff --git a/JobFlow.Business/Services/EstimateService.cs b/JobFlow.Business/Services/EstimateService.cs index 2e5112b..d5b7d44 100644 --- a/JobFlow.Business/Services/EstimateService.cs +++ b/JobFlow.Business/Services/EstimateService.cs @@ -19,6 +19,7 @@ public class EstimateService : IEstimateService private readonly IOrganizationClientPortalService clientPortalService; private readonly IFollowUpAutomationService? _followUpAutomation; private readonly IJobService _jobService; + private readonly IEstimateNumberGenerator _numberGenerator; private readonly IRepository estimates; private readonly IRepository clients; @@ -29,6 +30,7 @@ public EstimateService( IPdfGenerator pdfGenerator, IOrganizationClientPortalService clientPortalService, IJobService jobService, + IEstimateNumberGenerator numberGenerator, IFollowUpAutomationService? followUpAutomation = null) { this.unitOfWork = unitOfWork; @@ -36,6 +38,7 @@ public EstimateService( this.pdfGenerator = pdfGenerator; this.clientPortalService = clientPortalService; _jobService = jobService; + _numberGenerator = numberGenerator; _followUpAutomation = followUpAutomation; estimates = unitOfWork.RepositoryOf(); @@ -80,7 +83,7 @@ public async Task> CreateAsync(CreateEstimateRequest request { OrganizationId = request.OrganizationId, OrganizationClientId = request.OrganizationClientId, - EstimateNumber = await GenerateEstimateNumberAsync(request.OrganizationId), + EstimateNumber = await _numberGenerator.GenerateAsync(request.OrganizationId), Title = request.Title, Description = request.Description, Notes = request.Notes, @@ -125,6 +128,9 @@ public async Task> UpdateAsync(Guid id, UpdateEstimateReques if (estimate == null) return Result.Failure(EstimateErrors.NotFound); + if (estimate.Status is EstimateStatus.Sent or EstimateStatus.Accepted or EstimateStatus.Declined) + return Result.Failure(EstimateErrors.CannotEditInCurrentStatus); + estimate.Title = request.Title; estimate.Description = request.Description; estimate.Notes = request.Notes; @@ -315,13 +321,7 @@ private async Task CreateJobFromEstimateAsync(Estimate estimate) await _jobService.UpsertJobAsync(job, estimate.OrganizationId); } - private async Task GenerateEstimateNumberAsync(Guid organizationId) - { - var prefix = $"EST-{DateTime.UtcNow:yyyyMMdd}-"; - var count = await estimates.Query() - .CountAsync(x => x.OrganizationId == organizationId && x.EstimateNumber.StartsWith(prefix)); - return $"{prefix}{count + 1:0000}"; - } + private static string GeneratePublicToken() { diff --git a/JobFlow.Business/Services/InvoiceService.cs b/JobFlow.Business/Services/InvoiceService.cs index f47513d..ac89376 100644 --- a/JobFlow.Business/Services/InvoiceService.cs +++ b/JobFlow.Business/Services/InvoiceService.cs @@ -136,9 +136,18 @@ public async Task>> GetInvoicesByOrganiza var totalCount = await query.CountAsync(); - if (CursorToken.TryRead(cursor, out var cursorCreatedAt, out var cursorId)) + var sortKey = (sortBy ?? string.Empty).ToLowerInvariant(); + var useKeysetCursor = sortKey is "" or "createdat"; + + if (useKeysetCursor && CursorToken.TryRead(cursor, out var cursorCreatedAt, out var cursorId)) + { + query = desc + ? query.Where(i => i.CreatedAt < cursorCreatedAt || (i.CreatedAt == cursorCreatedAt && i.Id.CompareTo(cursorId) < 0)) + : query.Where(i => i.CreatedAt > cursorCreatedAt || (i.CreatedAt == cursorCreatedAt && i.Id.CompareTo(cursorId) > 0)); + } + else if (!useKeysetCursor && CursorToken.TryReadOffset(cursor, out var offset)) { - query = query.Where(i => i.CreatedAt < cursorCreatedAt || (i.CreatedAt == cursorCreatedAt && i.Id.CompareTo(cursorId) < 0)); + query = query.Skip(offset); } var batch = await query @@ -148,9 +157,20 @@ public async Task>> GetInvoicesByOrganiza var hasMore = batch.Count > size; var items = hasMore ? batch.Take(size).ToList() : batch; - var nextCursor = hasMore && items.Count > 0 - ? CursorToken.Build(items[^1].CreatedAt, items[^1].Id) - : null; + string? nextCursor; + if (!hasMore || items.Count == 0) + { + nextCursor = null; + } + else if (useKeysetCursor) + { + nextCursor = CursorToken.Build(items[^1].CreatedAt, items[^1].Id); + } + else + { + var currentOffset = CursorToken.TryReadOffset(cursor, out var prevOff) ? prevOff : 0; + nextCursor = CursorToken.BuildOffset(currentOffset + items.Count); + } return Result>.Success(new CursorPagedResponseDto { @@ -166,14 +186,34 @@ public async Task> GetInvoiceAggregatesByOrganizatio .AsNoTracking() .Where(i => i.OrganizationId == organizationId); - var invoiceCount = await baseQuery.CountAsync(); - var outstanding = await baseQuery - .Where(i => i.Status != InvoiceStatus.Paid) - .SumAsync(i => (decimal?)(i.TotalAmount - i.AmountPaid)) ?? 0m; + var statusCounts = await baseQuery + .GroupBy(i => i.Status) + .Select(g => new { Status = g.Key, Count = g.Count() }) + .ToListAsync(); + + var totals = await baseQuery + .Select(i => new { i.TotalAmount, i.AmountPaid, i.Status }) + .ToListAsync(); + + var invoiceCount = totals.Count; + var totalBilled = totals.Sum(i => i.TotalAmount); + var balanceDue = totals.Sum(i => i.TotalAmount - i.AmountPaid); + var outstanding = totals + .Where(i => i.Status != InvoiceStatus.Paid && i.Status != InvoiceStatus.Refunded) + .Sum(i => i.TotalAmount - i.AmountPaid); + + int CountFor(InvoiceStatus s) => statusCounts.FirstOrDefault(x => x.Status == s)?.Count ?? 0; return Result.Success(new InvoiceAggregateDto { InvoiceCount = invoiceCount, + DraftCount = CountFor(InvoiceStatus.Draft), + SentCount = CountFor(InvoiceStatus.Sent), + PaidCount = CountFor(InvoiceStatus.Paid), + OverdueCount = CountFor(InvoiceStatus.Overdue), + RefundedCount = CountFor(InvoiceStatus.Refunded), + TotalBilled = totalBilled, + BalanceDue = balanceDue, Outstanding = outstanding }); } @@ -305,7 +345,7 @@ public async Task> MarkPaidAsync( return Result.Success(invoice); invoice.Status = InvoiceStatus.Paid; - invoice.AmountPaid = amountReceived; + invoice.AmountPaid += amountReceived; invoice.PaidAt = DateTimeOffset.UtcNow; invoice.PaymentProvider = provider; invoice.ExternalPaymentId = externalPaymentId; @@ -358,6 +398,41 @@ public async Task> RecordDepositAsync( return Result.Success(invoice); } + public async Task> RecordRefundAsync( + Guid invoiceId, + decimal refundAmount) + { + var invoice = await invoices.Query() + .Include(e => e.OrganizationClient) + .FirstOrDefaultAsync(i => i.Id == invoiceId); + + if (invoice == null) + return Result.Failure(InvoiceErrors.NotFound); + + if (refundAmount <= 0) + return Result.Failure(InvoiceErrors.InvalidAmount); + + if (invoice.AmountRefunded + refundAmount > invoice.TotalAmount) + return Result.Failure(InvoiceErrors.RefundExceedsTotal); + + invoice.AmountRefunded += refundAmount; + invoice.AmountPaid = Math.Max(invoice.AmountPaid - refundAmount, 0); + + if (invoice.AmountRefunded >= invoice.TotalAmount) + { + invoice.Status = InvoiceStatus.Refunded; + } + else if (invoice.AmountPaid < invoice.TotalAmount) + { + invoice.Status = InvoiceStatus.Sent; + } + + invoices.Update(invoice); + await unitOfWork.SaveChangesAsync(); + + return Result.Success(invoice); + } + private async Task SendInvoiceToClientAsync(Invoice invoice) { var client = invoice.OrganizationClient; @@ -435,7 +510,7 @@ private async Task> CreateInvoiceFromEstimateAsync(Guid organiza Id = Guid.NewGuid(), PriceBookItemId = li.PriceBookItemId, Description = string.IsNullOrWhiteSpace(li.Description) ? li.Name : li.Description, - Quantity = (int)Math.Round(li.Quantity, MidpointRounding.AwayFromZero), + Quantity = li.Quantity, UnitPrice = li.UnitPrice }).ToList() }; diff --git a/JobFlow.Business/Services/JobService.cs b/JobFlow.Business/Services/JobService.cs index 15391c9..326caa2 100644 --- a/JobFlow.Business/Services/JobService.cs +++ b/JobFlow.Business/Services/JobService.cs @@ -246,9 +246,18 @@ public async Task>> GetJobsPagedAsync( var totalCount = await query.CountAsync(); - if (CursorToken.TryRead(cursor, out var cursorCreatedAt, out var cursorId)) + var sortKey = (sortBy ?? string.Empty).ToLowerInvariant(); + var useKeysetCursor = sortKey is "" or "createdat"; + + if (useKeysetCursor && CursorToken.TryRead(cursor, out var cursorCreatedAt, out var cursorId)) + { + query = desc + ? query.Where(j => j.CreatedAt < cursorCreatedAt || (j.CreatedAt == cursorCreatedAt && j.Id.CompareTo(cursorId) < 0)) + : query.Where(j => j.CreatedAt > cursorCreatedAt || (j.CreatedAt == cursorCreatedAt && j.Id.CompareTo(cursorId) > 0)); + } + else if (!useKeysetCursor && CursorToken.TryReadOffset(cursor, out var offset)) { - query = query.Where(j => j.CreatedAt < cursorCreatedAt || (j.CreatedAt == cursorCreatedAt && j.Id.CompareTo(cursorId) < 0)); + query = query.Skip(offset); } var batch = await query @@ -304,9 +313,20 @@ public async Task>> GetJobsPagedAsync( } }).ToList(); - var nextCursor = hasMore && items.Count > 0 - ? CursorToken.Build(items[^1].CreatedAt, items[^1].Id) - : null; + string? nextCursor; + if (!hasMore || items.Count == 0) + { + nextCursor = null; + } + else if (useKeysetCursor) + { + nextCursor = CursorToken.Build(items[^1].CreatedAt, items[^1].Id); + } + else + { + var currentOffset = CursorToken.TryReadOffset(cursor, out var prevOff) ? prevOff : 0; + nextCursor = CursorToken.BuildOffset(currentOffset + items.Count); + } return Result.Success(new CursorPagedResponseDto { @@ -354,10 +374,11 @@ await onboardingService.MarkStepCompleteAsync( return Result.Success(model); } - public async Task DeleteJobAsync(Guid id) + public async Task DeleteJobAsync(Guid id, Guid organizationId) { var job = await jobs.Query() - .FirstOrDefaultAsync(j => j.Id == id); + .Include(j => j.OrganizationClient) + .FirstOrDefaultAsync(j => j.Id == id && j.OrganizationClient.OrganizationId == organizationId); if (job == null) return Result.Failure(JobErrors.NotFound); diff --git a/JobFlow.Business/Services/PriceBookItemService.cs b/JobFlow.Business/Services/PriceBookItemService.cs index 4779517..07bcc8b 100644 --- a/JobFlow.Business/Services/PriceBookItemService.cs +++ b/JobFlow.Business/Services/PriceBookItemService.cs @@ -64,16 +64,8 @@ public async Task>> GetAllAsync(Guid organizationI public async Task> CreateAsync(PriceBookItem item) { - try - { - await _uow.RepositoryOf().AddAsync(item); - await _uow.SaveChangesAsync(); - } - catch (Exception ex) - { - var message = ex.Message; - throw; - } + await _uow.RepositoryOf().AddAsync(item); + await _uow.SaveChangesAsync(); return item; } diff --git a/JobFlow.Business/Services/ServiceInterfaces/IEstimateNumberGenerator.cs b/JobFlow.Business/Services/ServiceInterfaces/IEstimateNumberGenerator.cs new file mode 100644 index 0000000..d1c730d --- /dev/null +++ b/JobFlow.Business/Services/ServiceInterfaces/IEstimateNumberGenerator.cs @@ -0,0 +1,10 @@ +namespace JobFlow.Business.Services.ServiceInterfaces; + +public interface IEstimateNumberGenerator +{ + /// + /// Generates a unique, sequential estimate number scoped per organization and date. + /// Format: EST-YYYYMMDD-0001, EST-YYYYMMDD-0002, etc. + /// + Task GenerateAsync(Guid organizationId); +} diff --git a/JobFlow.Business/Services/ServiceInterfaces/IInvoiceService.cs b/JobFlow.Business/Services/ServiceInterfaces/IInvoiceService.cs index 51f93f8..e72310a 100644 --- a/JobFlow.Business/Services/ServiceInterfaces/IInvoiceService.cs +++ b/JobFlow.Business/Services/ServiceInterfaces/IInvoiceService.cs @@ -36,4 +36,8 @@ Task> RecordDepositAsync( decimal depositAmount, PaymentProvider provider, string externalPaymentId); + + Task> RecordRefundAsync( + Guid invoiceId, + decimal refundAmount); } \ No newline at end of file diff --git a/JobFlow.Business/Services/ServiceInterfaces/IJobService.cs b/JobFlow.Business/Services/ServiceInterfaces/IJobService.cs index 7c6a6bb..5001730 100644 --- a/JobFlow.Business/Services/ServiceInterfaces/IJobService.cs +++ b/JobFlow.Business/Services/ServiceInterfaces/IJobService.cs @@ -11,7 +11,7 @@ public interface IJobService Task>> GetJobsByStatusAsync(Guid organizationId, JobLifecycleStatus status); Task>> GetJobsForClientAsync(Guid organizationId, Guid organizationClientId); Task> UpsertJobAsync(Job model, Guid organizationId); - Task DeleteJobAsync(Guid id); + Task DeleteJobAsync(Guid id, Guid organizationId); Task>> GetJobsAsync(Guid organizationId); Task>> GetJobsPagedAsync( Guid organizationId, diff --git a/JobFlow.Business/Services/SupportHubInviteService.cs b/JobFlow.Business/Services/SupportHubInviteService.cs index 5fc60d5..3b047fe 100644 --- a/JobFlow.Business/Services/SupportHubInviteService.cs +++ b/JobFlow.Business/Services/SupportHubInviteService.cs @@ -145,10 +145,9 @@ private static string GenerateCode() { const string alphabet = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; Span chars = stackalloc char[8]; - var random = new Random(); for (var i = 0; i < chars.Length; i += 1) { - chars[i] = alphabet[random.Next(alphabet.Length)]; + chars[i] = alphabet[System.Security.Cryptography.RandomNumberGenerator.GetInt32(alphabet.Length)]; } return new string(chars); diff --git a/JobFlow.Domain/Enums/InvoiceStatus.cs b/JobFlow.Domain/Enums/InvoiceStatus.cs index 8303460..cef4caf 100644 --- a/JobFlow.Domain/Enums/InvoiceStatus.cs +++ b/JobFlow.Domain/Enums/InvoiceStatus.cs @@ -5,5 +5,6 @@ public enum InvoiceStatus Draft, Sent, Paid, - Overdue + Overdue, + Refunded } \ No newline at end of file diff --git a/JobFlow.Domain/Models/EstimateSequence.cs b/JobFlow.Domain/Models/EstimateSequence.cs new file mode 100644 index 0000000..1084e67 --- /dev/null +++ b/JobFlow.Domain/Models/EstimateSequence.cs @@ -0,0 +1,10 @@ +namespace JobFlow.Domain.Models; + +public class EstimateSequence : Entity +{ + public Guid OrganizationId { get; set; } + public int Year { get; set; } + public int Month { get; set; } + public int Day { get; set; } + public int LastSequence { get; set; } +} diff --git a/JobFlow.Domain/Models/Invoice.cs b/JobFlow.Domain/Models/Invoice.cs index ae44dc1..5b0e1b2 100644 --- a/JobFlow.Domain/Models/Invoice.cs +++ b/JobFlow.Domain/Models/Invoice.cs @@ -14,6 +14,7 @@ public class Invoice : Entity public DateTime DueDate { get; set; } public decimal TotalAmount { get; set; } public decimal AmountPaid { get; set; } + public decimal AmountRefunded { get; set; } public decimal BalanceDue => TotalAmount - AmountPaid; public InvoiceStatus Status { get; set; } diff --git a/JobFlow.Domain/Models/InvoiceLineItem.cs b/JobFlow.Domain/Models/InvoiceLineItem.cs index 93445ae..02b8d35 100644 --- a/JobFlow.Domain/Models/InvoiceLineItem.cs +++ b/JobFlow.Domain/Models/InvoiceLineItem.cs @@ -5,7 +5,7 @@ public class InvoiceLineItem : Entity public Guid InvoiceId { get; set; } public Guid? PriceBookItemId { get; set; } public string Description { get; set; } = null!; - public int Quantity { get; set; } + public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal LineTotal => UnitPrice * Quantity; [System.Text.Json.Serialization.JsonIgnore] diff --git a/JobFlow.Infrastructure.Persistence/Configurations/EstimateSequenceConfiguration.cs b/JobFlow.Infrastructure.Persistence/Configurations/EstimateSequenceConfiguration.cs new file mode 100644 index 0000000..d558bdd --- /dev/null +++ b/JobFlow.Infrastructure.Persistence/Configurations/EstimateSequenceConfiguration.cs @@ -0,0 +1,14 @@ +using JobFlow.Domain.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace JobFlow.Infrastructure.Persistence.Configurations; + +internal class EstimateSequenceConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("EstimateSequence"); + builder.HasKey(e => e.Id); + } +} diff --git a/JobFlow.Infrastructure.Persistence/JobFlowDbContext.cs b/JobFlow.Infrastructure.Persistence/JobFlowDbContext.cs index a1ffadf..05e6a1f 100644 --- a/JobFlow.Infrastructure.Persistence/JobFlowDbContext.cs +++ b/JobFlow.Infrastructure.Persistence/JobFlowDbContext.cs @@ -17,6 +17,7 @@ public JobFlowDbContext(DbContextOptions options) : base(options) public DbSet JobUpdates { get; set; } public DbSet JobUpdateAttachments { get; set; } public DbSet InvoiceSequences { get; set; } + public DbSet EstimateSequences { get; set; } public DbSet Organizations { get; set; } public DbSet OrganizationBrandings { get; set; } public DbSet OrganizationTypes { get; set; } diff --git a/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.Designer.cs b/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.Designer.cs new file mode 100644 index 0000000..dfcb983 --- /dev/null +++ b/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.Designer.cs @@ -0,0 +1,5997 @@ +// +using System; +using JobFlow.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JobFlow.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(JobFlowDbContext))] + [Migration("20260414015449_AddInvoiceAmountRefunded")] + partial class AddInvoiceAmountRefunded + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualEnd") + .HasColumnType("datetimeoffset"); + + b.Property("ActualStart") + .HasColumnType("datetimeoffset"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduleType") + .HasColumnType("int"); + + b.Property("ScheduledEnd") + .HasColumnType("datetimeoffset"); + + b.Property("ScheduledStart") + .HasColumnType("datetimeoffset"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.HasIndex("ScheduledStart"); + + b.ToTable("Assignment", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentAssignee", b => + { + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsLead") + .HasColumnType("bit"); + + b.HasKey("AssignmentId", "EmployeeId"); + + b.HasIndex("EmployeeId") + .HasDatabaseName("IX_AssignmentAssignee_EmployeeId"); + + b.ToTable("AssignmentAssignee", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("ChangedAt") + .HasColumnType("datetime2"); + + b.Property("ChangedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EventType") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("NewScheduledEnd") + .HasColumnType("datetime2"); + + b.Property("NewScheduledStart") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OldScheduledEnd") + .HasColumnType("datetime2"); + + b.Property("OldScheduledStart") + .HasColumnType("datetime2"); + + b.Property("Reason") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId") + .HasDatabaseName("IX_AssignmentHistory_AssignmentId"); + + b.ToTable("AssignmentHistory", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentOrder", b => + { + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.HasKey("AssignmentId", "OrderId"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("OrderId"); + + b.ToTable("AssignmentOrder", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DetailsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Method") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Path") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ResourceId") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("ResourceType") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("UserId") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("UserId", "CreatedAt"); + + b.ToTable("AuditLog", "security"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ChangelogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("PublishedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("IsPublished"); + + b.ToTable("ChangelogEntries"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("FailedRows") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProcessedRows") + .HasColumnType("int"); + + b.Property("SourceSystem") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("SucceededRows") + .HasColumnType("int"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.ToTable("ClientImportJob", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJobError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientImportJobId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientImportJobId"); + + b.HasIndex("RowNumber"); + + b.ToTable("ClientImportJobError", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientImportUploadSessionId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RowDataJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientImportUploadSessionId", "RowNumber") + .IsUnique(); + + b.ToTable("ClientImportUploadRow", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConsumedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceSystem") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Status", "ExpiresAtUtc"); + + b.ToTable("ClientImportUploadSession", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Conversation", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ConversationParticipant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("ConversationId", "UserId") + .IsUnique(); + + b.ToTable("ConversationParticipant", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.CustomerPaymentProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultPaymentMethodId") + .HasColumnType("nvarchar(max)"); + + b.Property("EncryptedAccessToken") + .HasColumnType("nvarchar(max)"); + + b.Property("EncryptedRefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDelinquent") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OwnerId") + .HasColumnType("uniqueidentifier"); + + b.Property("OwnerType") + .HasColumnType("int"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ProviderCustomerId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SquareLocationId") + .HasColumnType("nvarchar(max)"); + + b.Property("TokenExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("CustomerPaymentProfile", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.DataExportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ContentType") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DownloadCount") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("FileContent") + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("RequestedByUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("OrganizationId", "Status"); + + b.ToTable("DataExportJob", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("HireDate") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProfilePictureUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TerminationDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("Employees", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ErrorMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("FailedRows") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProcessedRows") + .HasColumnType("int"); + + b.Property("SourceSystem") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SucceededRows") + .HasColumnType("int"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeImportJobs"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJobError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeImportJobId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeImportJobId"); + + b.ToTable("EmployeeImportJobErrors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeImportUploadSessionId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RowDataJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeImportUploadSessionId"); + + b.ToTable("EmployeeImportUploadRows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConsumedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceSystem") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeImportUploadSessions"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("AccessIpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("AccessedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("InviteToken") + .HasMaxLength(128) + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("ShortCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InviteToken") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.HasIndex("RoleId"); + + b.HasIndex("ShortCode") + .IsUnique() + .HasFilter("[ShortCode] IS NOT NULL"); + + b.HasIndex("OrganizationId", "Email") + .IsUnique() + .HasFilter("[Status] = 1"); + + b.HasIndex("Status", "ExpiresAt"); + + b.ToTable("EmployeeInvites", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeRoles", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IndustryKey") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeRolePresets", (string)null); + + b.HasData( + new + { + Id = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for field service teams.", + IndustryKey = "home-services", + IsActive = true, + IsSystem = true, + Name = "Home services" + }, + new + { + Id = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for creative studios.", + IndustryKey = "creative", + IsActive = true, + IsSystem = true, + Name = "Creative" + }, + new + { + Id = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for consulting teams.", + IndustryKey = "consulting", + IsActive = true, + IsSystem = true, + Name = "Consulting" + }, + new + { + Id = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for repair shops.", + IndustryKey = "tech-repair", + IsActive = true, + IsSystem = true, + Name = "Tech repair" + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePresetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("PresetId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PresetId"); + + b.ToTable("EmployeeRolePresetItems", (string)null); + + b.HasData( + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111111"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Field technician for on-site work.", + IsActive = true, + Name = "Technician", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111112"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lead for quality checks and approvals.", + IsActive = true, + Name = "Supervisor", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111113"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Routes schedules and job assignments.", + IsActive = true, + Name = "Dispatcher", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111114"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Back-office support and billing.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222221"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Primary creator and deliverable owner.", + IsActive = true, + Name = "Designer", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222222"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Owns timelines, approvals, and client comms.", + IsActive = true, + Name = "Producer", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222223"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Schedules tasks and supports delivery.", + IsActive = true, + Name = "Coordinator", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222224"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Operations and billing support.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333331"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Client-facing delivery specialist.", + IsActive = true, + Name = "Consultant", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333332"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Owns engagement delivery and quality.", + IsActive = true, + Name = "Lead", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333333"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Plans meetings and follow-ups.", + IsActive = true, + Name = "Coordinator", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333334"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Back-office support and billing.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444441"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Executes diagnostics and repairs.", + IsActive = true, + Name = "Repair Tech", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444442"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Final testing and release approvals.", + IsActive = true, + Name = "QA", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444443"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Client intake and status updates.", + IsActive = true, + Name = "Service Advisor", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444444"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Operations and billing support.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 4 + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EstimateNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PublicToken") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTokenExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("SentAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasColumnType("decimal(18,2)"); + + b.Property("TaxTotal") + .HasColumnType("decimal(18,2)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("PublicToken") + .IsUnique(); + + b.HasIndex("OrganizationId", "EstimateNumber") + .IsUnique(); + + b.ToTable("Estimates", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateLineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PriceBookItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateId"); + + b.HasIndex("PriceBookItemId"); + + b.ToTable("EstimateLineItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateRevisionRequestId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("nvarchar(260)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateRevisionRequestId"); + + b.ToTable("EstimateRevisionAttachments", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationResponseMessage") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("RequestMessage") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("RequestedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ResolvedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ReviewedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RevisionNumber") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("EstimateId", "RevisionNumber") + .IsUnique(); + + b.ToTable("EstimateRevisionRequests", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpExecutionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AttemptedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Channel") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FailureReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FollowUpRunId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ScheduledFor") + .HasColumnType("datetimeoffset"); + + b.Property("StepOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("WasSent") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpRunId", "StepOrder"); + + b.ToTable("FollowUpExecutionLogs", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FollowUpSequenceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastAttemptAt") + .HasColumnType("datetimeoffset"); + + b.Property("NextStepOrder") + .HasColumnType("int"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SequenceType") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StopReason") + .HasColumnType("int"); + + b.Property("TriggerEntityId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpSequenceId", "OrganizationClientId", "Status"); + + b.HasIndex("OrganizationId", "SequenceType", "TriggerEntityId", "Status"); + + b.ToTable("FollowUpRuns", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultChannel") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SequenceType") + .HasColumnType("int"); + + b.Property("StopOnClientReply") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "SequenceType", "IsActive"); + + b.ToTable("FollowUpSequences", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ChannelOverride") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DelayHours") + .HasColumnType("int"); + + b.Property("FollowUpSequenceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsEscalation") + .HasColumnType("bit"); + + b.Property("MessageTemplate") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("StepOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpSequenceId", "StepOrder") + .IsUnique(); + + b.ToTable("FollowUpSteps", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.HelpArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ArticleType") + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsFeatured") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("PublishedAt") + .HasColumnType("datetimeoffset"); + + b.Property("PublishedBy") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ArticleType"); + + b.HasIndex("Category"); + + b.HasIndex("IsPublished"); + + b.ToTable("HelpArticles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CostPerUnit") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("QuantityInStock") + .HasColumnType("int"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("InventoryItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AmountPaid") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("AmountRefunded") + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExternalPaymentId") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime2"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaidAt") + .HasColumnType("datetimeoffset"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.HasIndex("OrderId"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("OrganizationId", "Status"); + + b.ToTable("Invoice"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceLineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PriceBookItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.Property("UnitPrice") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.HasIndex("PriceBookItemId"); + + b.ToTable("InvoiceLineItem", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastSequence") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("InvoiceSequence", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Comments") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("InvoicingWorkflow") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("float"); + + b.Property("LifecycleStatus") + .HasColumnType("int"); + + b.Property("Longitude") + .HasColumnType("float"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateId"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Job", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobRecurrence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DaysOfWeekMask") + .HasColumnType("int"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Duration") + .HasColumnType("time"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("Frequency") + .HasColumnType("int"); + + b.Property("GenerateDaysAhead") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("ScheduleType") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("StartTime") + .HasColumnType("time"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId", "IsActive") + .HasDatabaseName("IX_JobRecurrence_JobId_IsActive"); + + b.ToTable("JobRecurrence", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultInvoicingWorkflow") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("OrganizationTypeId"); + + b.ToTable("JobTemplates", (string)null); + + b.HasData( + new + { + Id = new Guid("3a3b3c3d-0101-0101-0101-010101010101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Full or partial home remodel project.", + IsActive = true, + IsSystem = true, + Name = "Home renovation", + OrganizationTypeId = new Guid("bf489aa6-db19-42df-82bc-c116bd967e7e") + }, + new + { + Id = new Guid("3a3b3c3d-0101-0101-0101-010101010102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "New outdoor living space construction.", + IsActive = true, + IsSystem = true, + Name = "Deck / patio build", + OrganizationTypeId = new Guid("bf489aa6-db19-42df-82bc-c116bd967e7e") + }, + new + { + Id = new Guid("3a3b3c3d-0202-0202-0202-020202020201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Paint walls, ceilings, and trim for interior rooms.", + IsActive = true, + IsSystem = true, + Name = "Interior painting", + OrganizationTypeId = new Guid("393a5b3e-323e-4b76-aa86-0d4683ddcd49") + }, + new + { + Id = new Guid("3a3b3c3d-0202-0202-0202-020202020202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Prep and paint home exterior surfaces.", + IsActive = true, + IsSystem = true, + Name = "Exterior painting", + OrganizationTypeId = new Guid("393a5b3e-323e-4b76-aa86-0d4683ddcd49") + }, + new + { + Id = new Guid("3a3b3c3d-0303-0303-0303-030303030301"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Diagnose and fix leaks, clogs, or faults.", + IsActive = true, + IsSystem = true, + Name = "Plumbing repair", + OrganizationTypeId = new Guid("e01750b0-0d01-4e25-abf7-6efa23509035") + }, + new + { + Id = new Guid("3a3b3c3d-0303-0303-0303-030303030302"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Remove old unit and install replacement.", + IsActive = true, + IsSystem = true, + Name = "Water heater install", + OrganizationTypeId = new Guid("e01750b0-0d01-4e25-abf7-6efa23509035") + }, + new + { + Id = new Guid("3a3b3c3d-0404-0404-0404-040404040401"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Mow, edge, blow, and treat lawn.", + IsActive = true, + IsSystem = true, + Name = "Lawn maintenance", + OrganizationTypeId = new Guid("fbc6accf-0fb1-4908-b449-14f13b826f24") + }, + new + { + Id = new Guid("3a3b3c3d-0404-0404-0404-040404040402"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Design beds, select plants, and install.", + IsActive = true, + IsSystem = true, + Name = "Garden design & install", + OrganizationTypeId = new Guid("fbc6accf-0fb1-4908-b449-14f13b826f24") + }, + new + { + Id = new Guid("3a3b3c3d-0505-0505-0505-050505050501"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Troubleshoot and fix wiring or fixture issues.", + IsActive = true, + IsSystem = true, + Name = "Electrical repair", + OrganizationTypeId = new Guid("9362c957-0f41-4c20-9085-c01e449fdda2") + }, + new + { + Id = new Guid("3a3b3c3d-0505-0505-0505-050505050502"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Upgrade electrical panel for capacity or safety.", + IsActive = true, + IsSystem = true, + Name = "Panel upgrade", + OrganizationTypeId = new Guid("9362c957-0f41-4c20-9085-c01e449fdda2") + }, + new + { + Id = new Guid("3a3b3c3d-0606-0606-0606-060606060601"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Measure, build, and install custom shelves.", + IsActive = true, + IsSystem = true, + Name = "Custom shelving", + OrganizationTypeId = new Guid("8f0d3e93-425b-4a53-b4d2-4c5eb97e490f") + }, + new + { + Id = new Guid("3a3b3c3d-0606-0606-0606-060606060602"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Install baseboards, crown molding, and casings.", + IsActive = true, + IsSystem = true, + Name = "Trim & molding install", + OrganizationTypeId = new Guid("8f0d3e93-425b-4a53-b4d2-4c5eb97e490f") + }, + new + { + Id = new Guid("3a3b3c3d-0707-0707-0707-070707070701"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Seasonal heating/cooling system tune-up.", + IsActive = true, + IsSystem = true, + Name = "HVAC maintenance", + OrganizationTypeId = new Guid("37fc17e8-0a25-4119-9a71-7d160bb9c7b4") + }, + new + { + Id = new Guid("3a3b3c3d-0707-0707-0707-070707070702"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Remove old unit and install new AC system.", + IsActive = true, + IsSystem = true, + Name = "AC install / replacement", + OrganizationTypeId = new Guid("37fc17e8-0a25-4119-9a71-7d160bb9c7b4") + }, + new + { + Id = new Guid("3a3b3c3d-0808-0808-0808-080808080801"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Fell, section, and haul away tree.", + IsActive = true, + IsSystem = true, + Name = "Tree removal", + OrganizationTypeId = new Guid("f64f078f-ecfb-4f3e-8640-236219fcf01e") + }, + new + { + Id = new Guid("3a3b3c3d-0808-0808-0808-080808080802"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Prune branches for health and clearance.", + IsActive = true, + IsSystem = true, + Name = "Tree trimming", + OrganizationTypeId = new Guid("f64f078f-ecfb-4f3e-8640-236219fcf01e") + }, + new + { + Id = new Guid("3a3b3c3d-0909-0909-0909-090909090901"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Interior and exterior spray for common pests.", + IsActive = true, + IsSystem = true, + Name = "General pest treatment", + OrganizationTypeId = new Guid("bf3b9512-8a9c-4a73-9f88-cb914c1573cd") + }, + new + { + Id = new Guid("3a3b3c3d-0909-0909-0909-090909090902"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Full property inspection and treatment plan.", + IsActive = true, + IsSystem = true, + Name = "Termite inspection", + OrganizationTypeId = new Guid("bf3b9512-8a9c-4a73-9f88-cb914c1573cd") + }, + new + { + Id = new Guid("3a3b3c3d-1010-1010-1010-101010101001"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Standard whole-house cleaning visit.", + IsActive = true, + IsSystem = true, + Name = "Full home clean", + OrganizationTypeId = new Guid("1921d982-22f8-4ed5-b4e3-fca82c5767eb") + }, + new + { + Id = new Guid("3a3b3c3d-1010-1010-1010-101010101002"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Thorough clean for tenant turnover.", + IsActive = true, + IsSystem = true, + Name = "Move-out deep clean", + OrganizationTypeId = new Guid("1921d982-22f8-4ed5-b4e3-fca82c5767eb") + }, + new + { + Id = new Guid("3a3b3c3d-1111-1111-1111-111111111101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Remove all unwanted items from property.", + IsActive = true, + IsSystem = true, + Name = "Full property cleanout", + OrganizationTypeId = new Guid("408d2185-53b9-493d-8713-938114de90f5") + }, + new + { + Id = new Guid("3a3b3c3d-1111-1111-1111-111111111102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Clear and haul garage contents.", + IsActive = true, + IsSystem = true, + Name = "Garage cleanout", + OrganizationTypeId = new Guid("408d2185-53b9-493d-8713-938114de90f5") + }, + new + { + Id = new Guid("3a3b3c3d-1212-1212-1212-121212121201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Complete interior and exterior detail.", + IsActive = true, + IsSystem = true, + Name = "Full detail", + OrganizationTypeId = new Guid("33341b2d-957f-4efb-94f7-3a015ae1a718") + }, + new + { + Id = new Guid("3a3b3c3d-1212-1212-1212-121212121202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Deep clean seats, dash, carpet, and glass.", + IsActive = true, + IsSystem = true, + Name = "Interior only", + OrganizationTypeId = new Guid("33341b2d-957f-4efb-94f7-3a015ae1a718") + }, + new + { + Id = new Guid("3a3b3c3d-1313-1313-1313-131313131301"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Install and configure router, switches, and cabling.", + IsActive = true, + IsSystem = true, + Name = "Network setup", + OrganizationTypeId = new Guid("30530a32-a151-436d-a050-613eac4c22d5") + }, + new + { + Id = new Guid("3a3b3c3d-1313-1313-1313-131313131302"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Set up desktops, laptops, and peripherals.", + IsActive = true, + IsSystem = true, + Name = "Workstation deployment", + OrganizationTypeId = new Guid("30530a32-a151-436d-a050-613eac4c22d5") + }, + new + { + Id = new Guid("3a3b3c3d-1414-1414-1414-141414141401"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Fix miscellaneous issues around the home.", + IsActive = true, + IsSystem = true, + Name = "General repair", + OrganizationTypeId = new Guid("0f32e14a-5f70-45af-a647-04e59ad52e58") + }, + new + { + Id = new Guid("3a3b3c3d-1414-1414-1414-141414141402"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Mount and connect lights, fans, or hardware.", + IsActive = true, + IsSystem = true, + Name = "Fixture install", + OrganizationTypeId = new Guid("0f32e14a-5f70-45af-a647-04e59ad52e58") + }, + new + { + Id = new Guid("3a3b3c3d-1515-1515-1515-151515151501"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Measure, prep subfloor, and install hardwood.", + IsActive = true, + IsSystem = true, + Name = "Hardwood install", + OrganizationTypeId = new Guid("09786eab-d69f-45bf-bcec-5f368bd60be7") + }, + new + { + Id = new Guid("3a3b3c3d-1515-1515-1515-151515151502"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Lay tile with proper spacing and grout.", + IsActive = true, + IsSystem = true, + Name = "Tile install", + OrganizationTypeId = new Guid("09786eab-d69f-45bf-bcec-5f368bd60be7") + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplateItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TemplateId"); + + b.ToTable("JobTemplateItems", (string)null); + + b.HasData( + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Walk property and scope of work.", + IsActive = true, + Name = "Site assessment", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove old materials and prep surfaces.", + IsActive = true, + Name = "Demo & prep", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Construction, paint, and final touches.", + IsActive = true, + Name = "Build & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Draft plan and pull permits.", + IsActive = true, + Name = "Design & permits", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set footings, frame, and deck boards.", + IsActive = true, + Name = "Frame & build", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Stain/seal and final walkthrough.", + IsActive = true, + Name = "Finish & inspect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm colors, tape, and cover surfaces.", + IsActive = true, + Name = "Color consult & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Apply primer and two coats.", + IsActive = true, + Name = "Prime & paint", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Detail edges and remove coverings.", + IsActive = true, + Name = "Touch-up & clean", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean and prep exterior surfaces.", + IsActive = true, + Name = "Power wash & scrape", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Coat siding, fascia, and trim.", + IsActive = true, + Name = "Prime & paint", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check coverage and clean site.", + IsActive = true, + Name = "Final inspection", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Locate leak, blockage, or fault.", + IsActive = true, + Name = "Diagnose issue", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fix or replace the faulty component.", + IsActive = true, + Name = "Perform repair", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Run water, check pressure, tidy area.", + IsActive = true, + Name = "Test & clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Shut off supply and drain tank.", + IsActive = true, + Name = "Disconnect old unit", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Position, connect plumbing and power.", + IsActive = true, + Name = "Install new unit", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check for leaks and set temperature.", + IsActive = true, + Name = "Test & verify", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut grass and trim borders.", + IsActive = true, + Name = "Mow & edge", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clear clippings from walks and drives.", + IsActive = true, + Name = "Blow & clean", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Apply treatment as needed.", + IsActive = true, + Name = "Treat & fertilize", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Plan beds, paths, and plant selection.", + IsActive = true, + Name = "Design layout", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Amend soil and install plants.", + IsActive = true, + Name = "Prep & plant", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Top-dress and initial watering.", + IsActive = true, + Name = "Mulch & water", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Test circuits and locate issue.", + IsActive = true, + Name = "Diagnose fault", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Replace or repair faulty components.", + IsActive = true, + Name = "Repair wiring", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm power and safety.", + IsActive = true, + Name = "Test & verify", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Kill main and label circuits.", + IsActive = true, + Name = "Shut down & disconnect", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mount new box and reconnect breakers.", + IsActive = true, + Name = "Swap panel", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Restore power and run tests.", + IsActive = true, + Name = "Energize & inspect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Take dimensions and plan layout.", + IsActive = true, + Name = "Measure & design", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Build shelving units.", + IsActive = true, + Name = "Cut & assemble", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mount, sand, and apply finish.", + IsActive = true, + Name = "Install & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Measure runs and miter cut pieces.", + IsActive = true, + Name = "Measure & cut", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Attach molding and set nails.", + IsActive = true, + Name = "Nail & set", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fill gaps, prime, and paint.", + IsActive = true, + Name = "Caulk & paint", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check airflow and swap filter.", + IsActive = true, + Name = "Inspect & replace filter", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove buildup from outdoor unit.", + IsActive = true, + Name = "Clean condenser coils", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify settings and calibration.", + IsActive = true, + Name = "Test thermostat", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Disconnect and haul away.", + IsActive = true, + Name = "Remove old unit", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set unit, connect lines and electric.", + IsActive = true, + Name = "Install new system", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Add refrigerant and run cycles.", + IsActive = true, + Name = "Charge & test", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Evaluate drop zone and rigging.", + IsActive = true, + Name = "Assess & plan", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut tree and process limbs.", + IsActive = true, + Name = "Fell & section", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove debris and rake site.", + IsActive = true, + Name = "Haul & clean", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Identify dead or problem branches.", + IsActive = true, + Name = "Inspect canopy", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut for shape, clearance, and health.", + IsActive = true, + Name = "Prune branches", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Chip or haul brush.", + IsActive = true, + Name = "Clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Identify pest activity and entry points.", + IsActive = true, + Name = "Inspect property", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Spray interior and exterior perimeter.", + IsActive = true, + Name = "Apply treatment", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Log findings and next visit.", + IsActive = true, + Name = "Document & recommend", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check foundation, crawl, and attic.", + IsActive = true, + Name = "Full property scan", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Tap and test for damage.", + IsActive = true, + Name = "Probe suspect areas", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Findings and treatment estimate.", + IsActive = true, + Name = "Deliver report", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Assess rooms and special requests.", + IsActive = true, + Name = "Walkthrough", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Scrub fixtures, counters, appliances.", + IsActive = true, + Name = "Deep clean kitchen & baths", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "All hard and carpeted surfaces.", + IsActive = true, + Name = "Vacuum & mop", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clear remaining items, dust surfaces.", + IsActive = true, + Name = "Empty & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Walls, baseboards, inside cabinets.", + IsActive = true, + Name = "Scrub all rooms", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify quality before handoff.", + IsActive = true, + Name = "Final walkthrough", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Tag items and plan truck loads.", + IsActive = true, + Name = "Walk & inventory", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove items to truck.", + IsActive = true, + Name = "Load & haul", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean site, dump or donate.", + IsActive = true, + Name = "Sweep & dispose", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Separate keep, donate, and trash.", + IsActive = true, + Name = "Sort items", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Move discards to truck.", + IsActive = true, + Name = "Load out", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Broom clean and organize keepers.", + IsActive = true, + Name = "Sweep garage", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Hand wash, clay bar, and dry.", + IsActive = true, + Name = "Exterior wash & clay", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Vacuum, shampoo, and condition.", + IsActive = true, + Name = "Interior deep clean", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Compound, wax, and dress tires.", + IsActive = true, + Name = "Polish & protect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "All seats, dash, and crevices.", + IsActive = true, + Name = "Vacuum & dust", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Extract and clean fabric or leather.", + IsActive = true, + Name = "Shampoo upholstery", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean interior glass and surfaces.", + IsActive = true, + Name = "Glass & final wipe", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Map runs and plan equipment placement.", + IsActive = true, + Name = "Site survey", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Pull cable and install hardware.", + IsActive = true, + Name = "Run cable & mount", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set up network and verify connectivity.", + IsActive = true, + Name = "Configure & test", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Prep hardware and install OS/software.", + IsActive = true, + Name = "Unbox & image", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Place at desk and join network.", + IsActive = true, + Name = "Deploy & connect", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify apps and orient user.", + IsActive = true, + Name = "User walkthrough", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Inspect problem area and plan fix.", + IsActive = true, + Name = "Assess issue", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fix or replace components.", + IsActive = true, + Name = "Repair", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Test fix and tidy work area.", + IsActive = true, + Name = "Verify & clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mark placement and run wiring if needed.", + IsActive = true, + Name = "Prep location", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Secure and connect.", + IsActive = true, + Name = "Mount fixture", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm operation and patch holes.", + IsActive = true, + Name = "Test & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Level, clean, and lay moisture barrier.", + IsActive = true, + Name = "Prep subfloor", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lay and nail or click into place.", + IsActive = true, + Name = "Install planks", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Install transitions and clean.", + IsActive = true, + Name = "Trim & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Dry-fit pattern and mix thinset.", + IsActive = true, + Name = "Layout & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lay tiles with spacers.", + IsActive = true, + Name = "Set tile", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fill joints, clean haze, and seal.", + IsActive = true, + Name = "Grout & seal", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTracking", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Latitude") + .HasColumnType("decimal(9,6)"); + + b.Property("Longitude") + .HasColumnType("decimal(9,6)"); + + b.Property("RecordedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("JobId"); + + b.ToTable("JobTracking"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Message") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("OccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId", "OccurredAt"); + + b.ToTable("JobUpdates", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdateAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FileData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("nvarchar(260)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobUpdateId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobUpdateId"); + + b.ToTable("JobUpdateAttachments", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AttachmentUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExternalSenderName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalSenderPhone") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("ExternalSenderType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("SenderId") + .HasColumnType("uniqueidentifier"); + + b.Property("SentAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ConversationId"); + + b.HasIndex("SenderId"); + + b.ToTable("Message", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OrderDate") + .HasColumnType("datetime2"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TotalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Order", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("Address2") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactFirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactLastName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultTaxRate") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("EnableTax") + .HasColumnType("bit"); + + b.Property("HasFreeAccount") + .HasColumnType("bit"); + + b.Property("IndustryKey") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSquareConnected") + .HasColumnType("bit"); + + b.Property("IsStripeConnected") + .HasColumnType("bit"); + + b.Property("OnBoardingComplete") + .HasColumnType("bit"); + + b.Property("OnboardingPresetAppliedAt") + .HasColumnType("datetimeoffset"); + + b.Property("OnboardingPresetKey") + .HasColumnType("nvarchar(max)"); + + b.Property("OnboardingTrack") + .HasColumnType("nvarchar(max)"); + + b.Property("OnboardingTrackSelectedAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SquareMerchantId") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("StripeConnectAccountId") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionExpiresAt") + .HasColumnType("datetime2"); + + b.Property("SubscriptionPlanName") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationTypeId"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationBranding", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BusinessName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FooterNote") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PrimaryColor") + .HasColumnType("nvarchar(max)"); + + b.Property("SecondaryColor") + .HasColumnType("nvarchar(max)"); + + b.Property("Tagline") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationBranding", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("Address2") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(450)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "EmailAddress") + .IsUnique() + .HasFilter("[EmailAddress] IS NOT NULL AND [IsActive] = 1"); + + b.ToTable("OrganizationClient", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClientPortalSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("nvarchar(320)"); + + b.Property("ExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("RedeemedAt") + .HasColumnType("datetimeoffset"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.ToTable("OrganizationClientPortalSession"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationInvoicingSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultWorkflow") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("DepositPercentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("DepositRequired") + .HasColumnType("bit"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationInvoicingSettings"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationOnboardingStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "StepName") + .IsUnique(); + + b.ToTable("OrganizationOnboardingSteps", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationScheduleSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AutoNotifyReschedule") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultWindowMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(120); + + b.Property("EnforceTravelBuffer") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("TravelBufferMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(20); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationScheduleSettings"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ServiceName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationService", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OrganizationType", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationWorkflowStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StatusKey") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Category", "StatusKey") + .IsUnique(); + + b.ToTable("OrganizationWorkflowStatus"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PaymentHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AmountPaid") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EntityId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityType") + .HasColumnType("int"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("RawEventJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeInvoiceId") + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntentId") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.HasIndex("EntityId", "PaidAt"); + + b.ToTable("PaymentHistory"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("PriceBookCategories", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("Cost") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("InventoryItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("InventoryUnitsPerSale") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,4)") + .HasDefaultValue(1.0m); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsTaxable") + .HasColumnType("bit"); + + b.Property("ItemType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PartNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PricePerUnit") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("InventoryItemId"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("PriceBookItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SecurityAlert", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("DetailsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("EvidenceCount") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Severity") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("WindowEndUtc") + .HasColumnType("datetime2"); + + b.Property("WindowStartUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("RuleKey", "Status", "CreatedAt"); + + b.ToTable("SecurityAlert", "security"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ShortLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastAccessedAt") + .HasColumnType("datetime2"); + + b.Property("TargetUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique() + .HasFilter("[IsActive] = 1"); + + b.HasIndex("CreatedAt"); + + b.ToTable("ShortLink", "notifications"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SubscriptionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CanceledAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PaymentProfileId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlanName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ProviderPriceId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderSubscriptionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PaymentProfileId"); + + b.ToTable("SubscriptionRecord", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("nvarchar(12)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RedeemedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RedeemedByUid") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("SupportHubInvites"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AgentName") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SupportHubSessions"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubTicket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastActivityAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("nvarchar(160)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SupportHubTickets"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SystemRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", (string)null); + + b.HasData( + new + { + Id = new Guid("e88fbbe6-8bdf-4aca-b941-912785a94f0b"), + Name = "OrganizationAdmin" + }, + new + { + Id = new Guid("079e4277-0eb2-4222-82e4-5a751ede48f6"), + Name = "OrganizationEmployee" + }, + new + { + Id = new Guid("3da14c58-562a-437a-a2a6-47706b40eb70"), + Name = "OrganizationClient" + }, + new + { + Id = new Guid("5bc0d325-a915-4e17-8184-428ee533cf89"), + Name = "KatharixAdmin" + }, + new + { + Id = new Guid("92193eb2-dba0-433c-814e-9fca95bde016"), + Name = "KatharixEmployee" + }, + new + { + Id = new Guid("dfe36ebc-bfb5-4583-b68e-59be8ba60fa9"), + Name = "SuperAdmin" + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FirebaseUid") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PreferredLanguage") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.UserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("Assignments") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentAssignee", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany("AssignmentAssignees") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentHistory", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany() + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentOrder", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany("AssignmentOrders") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Order", "Order") + .WithMany("AssignmentOrders") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJobError", b => + { + b.HasOne("JobFlow.Domain.Models.ClientImportJob", "ClientImportJob") + .WithMany("Errors") + .HasForeignKey("ClientImportJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ClientImportJob"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadRow", b => + { + b.HasOne("JobFlow.Domain.Models.ClientImportUploadSession", "Session") + .WithMany("Rows") + .HasForeignKey("ClientImportUploadSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Session"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ConversationParticipant", b => + { + b.HasOne("JobFlow.Domain.Models.Conversation", "Conversation") + .WithMany("Participants") + .HasForeignKey("ConversationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Conversation"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.CustomerPaymentProfile", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", null) + .WithMany("PaymentProfiles") + .HasForeignKey("OrganizationClientId"); + + b.HasOne("JobFlow.Domain.Models.Organization", null) + .WithMany("PaymentProfiles") + .HasForeignKey("OrganizationId"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.DataExportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Employee", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("Employees") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.EmployeeRole", "Role") + .WithMany("Employees") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany("Employees") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Organization"); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJobError", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeImportJob", "EmployeeImportJob") + .WithMany("Errors") + .HasForeignKey("EmployeeImportJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EmployeeImportJob"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadRow", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeImportUploadSession", "Session") + .WithMany("Rows") + .HasForeignKey("EmployeeImportUploadSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Session"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeInvite", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.EmployeeRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("EmployeeRoles") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePresetItem", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeRolePreset", "Preset") + .WithMany("Items") + .HasForeignKey("PresetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Preset"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateLineItem", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany("LineItems") + .HasForeignKey("EstimateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.PriceBookItem", "PriceBookItem") + .WithMany() + .HasForeignKey("PriceBookItemId"); + + b.Navigation("Estimate"); + + b.Navigation("PriceBookItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionAttachment", b => + { + b.HasOne("JobFlow.Domain.Models.EstimateRevisionRequest", "RevisionRequest") + .WithMany("Attachments") + .HasForeignKey("EstimateRevisionRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RevisionRequest"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany("RevisionRequests") + .HasForeignKey("EstimateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Estimate"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpExecutionLog", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpRun", "Run") + .WithMany("ExecutionLogs") + .HasForeignKey("FollowUpRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpSequence", "Sequence") + .WithMany() + .HasForeignKey("FollowUpSequenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sequence"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpStep", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpSequence", "Sequence") + .WithMany("Steps") + .HasForeignKey("FollowUpSequenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sequence"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId"); + + b.HasOne("JobFlow.Domain.Models.Order", "Order") + .WithMany("Invoices") + .HasForeignKey("OrderId"); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("Order"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceLineItem", b => + { + b.HasOne("JobFlow.Domain.Models.Invoice", "Invoice") + .WithMany("LineItems") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.PriceBookItem", "PriceBookItem") + .WithMany() + .HasForeignKey("PriceBookItemId"); + + b.Navigation("Invoice"); + + b.Navigation("PriceBookItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany() + .HasForeignKey("EstimateId"); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany("Jobs") + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Estimate"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobRecurrence", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("JobFlow.Domain.Models.OrganizationType", "OrganizationType") + .WithMany() + .HasForeignKey("OrganizationTypeId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Organization"); + + b.Navigation("OrganizationType"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplateItem", b => + { + b.HasOne("JobFlow.Domain.Models.JobTemplate", "Template") + .WithMany("Items") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTracking", b => + { + b.HasOne("JobFlow.Domain.Models.User", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("JobTrackings") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("JobUpdates") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdateAttachment", b => + { + b.HasOne("JobFlow.Domain.Models.JobUpdate", "JobUpdate") + .WithMany("Attachments") + .HasForeignKey("JobUpdateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("JobUpdate"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Message", b => + { + b.HasOne("JobFlow.Domain.Models.Conversation", "Conversation") + .WithMany("Messages") + .HasForeignKey("ConversationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Conversation"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationType", "OrganizationType") + .WithMany() + .HasForeignKey("OrganizationTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OrganizationType"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationBranding", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClientPortalSession", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationOnboardingStep", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("OnboardingSteps") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationService", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PaymentHistory", b => + { + b.HasOne("JobFlow.Domain.Models.Invoice", "Invoice") + .WithMany("Payments") + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookItem", b => + { + b.HasOne("JobFlow.Domain.Models.PriceBookCategory", "Category") + .WithMany("Items") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("JobFlow.Domain.Models.InventoryItem", "InventoryItem") + .WithMany() + .HasForeignKey("InventoryItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Category"); + + b.Navigation("InventoryItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SubscriptionRecord", b => + { + b.HasOne("JobFlow.Domain.Models.CustomerPaymentProfile", "PaymentProfile") + .WithMany() + .HasForeignKey("PaymentProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PaymentProfile"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubTicket", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "Client") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.UserRole", b => + { + b.HasOne("JobFlow.Domain.Models.SystemRole", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.Navigation("AssignmentAssignees"); + + b.Navigation("AssignmentOrders"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.Navigation("Errors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.Navigation("Rows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.Navigation("Messages"); + + b.Navigation("Participants"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.Navigation("Errors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.Navigation("Rows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.Navigation("Employees"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.Navigation("LineItems"); + + b.Navigation("RevisionRequests"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.Navigation("ExecutionLogs"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpSequence", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.Navigation("LineItems"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.Navigation("Assignments"); + + b.Navigation("JobTrackings"); + + b.Navigation("JobUpdates"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.Navigation("AssignmentOrders"); + + b.Navigation("Invoices"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.Navigation("EmployeeRoles"); + + b.Navigation("Employees"); + + b.Navigation("OnboardingSteps"); + + b.Navigation("PaymentProfiles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.Navigation("Jobs"); + + b.Navigation("PaymentProfiles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookCategory", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SystemRole", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.Navigation("Employees"); + + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.cs b/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.cs new file mode 100644 index 0000000..3464c47 --- /dev/null +++ b/JobFlow.Infrastructure.Persistence/Migrations/20260414015449_AddInvoiceAmountRefunded.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JobFlow.Infrastructure.Persistence.Migrations +{ + /// + public partial class AddInvoiceAmountRefunded : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AmountRefunded", + table: "Invoice", + type: "decimal(18,2)", + nullable: false, + defaultValue: 0m); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AmountRefunded", + table: "Invoice"); + } + } +} diff --git a/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.Designer.cs b/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.Designer.cs new file mode 100644 index 0000000..f0a3e3d --- /dev/null +++ b/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.Designer.cs @@ -0,0 +1,6041 @@ +// +using System; +using JobFlow.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace JobFlow.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(JobFlowDbContext))] + [Migration("20260414024516_AppHardeningSchemaChanges")] + partial class AppHardeningSchemaChanges + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActualEnd") + .HasColumnType("datetimeoffset"); + + b.Property("ActualStart") + .HasColumnType("datetimeoffset"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduleType") + .HasColumnType("int"); + + b.Property("ScheduledEnd") + .HasColumnType("datetimeoffset"); + + b.Property("ScheduledStart") + .HasColumnType("datetimeoffset"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.HasIndex("ScheduledStart"); + + b.ToTable("Assignment", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentAssignee", b => + { + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("IsLead") + .HasColumnType("bit"); + + b.HasKey("AssignmentId", "EmployeeId"); + + b.HasIndex("EmployeeId") + .HasDatabaseName("IX_AssignmentAssignee_EmployeeId"); + + b.ToTable("AssignmentAssignee", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("ChangedAt") + .HasColumnType("datetime2"); + + b.Property("ChangedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EventType") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("NewScheduledEnd") + .HasColumnType("datetime2"); + + b.Property("NewScheduledStart") + .HasColumnType("datetime2"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OldScheduledEnd") + .HasColumnType("datetime2"); + + b.Property("OldScheduledStart") + .HasColumnType("datetime2"); + + b.Property("Reason") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId") + .HasDatabaseName("IX_AssignmentHistory_AssignmentId"); + + b.ToTable("AssignmentHistory", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentOrder", b => + { + b.Property("AssignmentId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.HasKey("AssignmentId", "OrderId"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("OrderId"); + + b.ToTable("AssignmentOrder", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DetailsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Method") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Path") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("ResourceId") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("ResourceType") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("Success") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("UserAgent") + .HasMaxLength(512) + .HasColumnType("nvarchar(512)"); + + b.Property("UserId") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("UserId", "CreatedAt"); + + b.ToTable("AuditLog", "security"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ChangelogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("PublishedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("IsPublished"); + + b.ToTable("ChangelogEntries"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("FailedRows") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProcessedRows") + .HasColumnType("int"); + + b.Property("SourceSystem") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("SucceededRows") + .HasColumnType("int"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.ToTable("ClientImportJob", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJobError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientImportJobId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientImportJobId"); + + b.HasIndex("RowNumber"); + + b.ToTable("ClientImportJobError", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientImportUploadSessionId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RowDataJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientImportUploadSessionId", "RowNumber") + .IsUnique(); + + b.ToTable("ClientImportUploadRow", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConsumedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceSystem") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Status", "ExpiresAtUtc"); + + b.ToTable("ClientImportUploadSession", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Conversation", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ConversationParticipant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("ConversationId", "UserId") + .IsUnique(); + + b.ToTable("ConversationParticipant", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.CustomerPaymentProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultPaymentMethodId") + .HasColumnType("nvarchar(max)"); + + b.Property("EncryptedAccessToken") + .HasColumnType("nvarchar(max)"); + + b.Property("EncryptedRefreshToken") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDelinquent") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OwnerId") + .HasColumnType("uniqueidentifier"); + + b.Property("OwnerType") + .HasColumnType("int"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ProviderCustomerId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SquareLocationId") + .HasColumnType("nvarchar(max)"); + + b.Property("TokenExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("CustomerPaymentProfile", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.DataExportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ContentType") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DownloadCount") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("FileContent") + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("RequestedByUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("OrganizationId", "Status"); + + b.ToTable("DataExportJob", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("HireDate") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobTitle") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("ProfilePictureUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("TerminationDate") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("RoleId"); + + b.HasIndex("UserId"); + + b.ToTable("Employees", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ErrorMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("FailedRows") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProcessedRows") + .HasColumnType("int"); + + b.Property("SourceSystem") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StartedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SucceededRows") + .HasColumnType("int"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeImportJobs"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJobError", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeImportJobId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Message") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeImportJobId"); + + b.ToTable("EmployeeImportJobErrors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadRow", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeImportUploadSessionId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RowDataJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("RowNumber") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeImportUploadSessionId"); + + b.ToTable("EmployeeImportUploadRows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConsumedAtUtc") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SourceSystem") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TotalRows") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeImportUploadSessions"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("AccessIpAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("AccessedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("InviteToken") + .HasMaxLength(128) + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("nvarchar(30)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.Property("ShortCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InviteToken") + .IsUnique(); + + b.HasIndex("OrganizationId"); + + b.HasIndex("RoleId"); + + b.HasIndex("ShortCode") + .IsUnique() + .HasFilter("[ShortCode] IS NOT NULL"); + + b.HasIndex("OrganizationId", "Email") + .IsUnique() + .HasFilter("[Status] = 1"); + + b.HasIndex("Status", "ExpiresAt"); + + b.ToTable("EmployeeInvites", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeRoles", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IndustryKey") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("EmployeeRolePresets", (string)null); + + b.HasData( + new + { + Id = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for field service teams.", + IndustryKey = "home-services", + IsActive = true, + IsSystem = true, + Name = "Home services" + }, + new + { + Id = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for creative studios.", + IndustryKey = "creative", + IsActive = true, + IsSystem = true, + Name = "Creative" + }, + new + { + Id = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for consulting teams.", + IndustryKey = "consulting", + IsActive = true, + IsSystem = true, + Name = "Consulting" + }, + new + { + Id = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Default roles for repair shops.", + IndustryKey = "tech-repair", + IsActive = true, + IsSystem = true, + Name = "Tech repair" + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePresetItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("PresetId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PresetId"); + + b.ToTable("EmployeeRolePresetItems", (string)null); + + b.HasData( + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111111"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Field technician for on-site work.", + IsActive = true, + Name = "Technician", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111112"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lead for quality checks and approvals.", + IsActive = true, + Name = "Supervisor", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111113"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Routes schedules and job assignments.", + IsActive = true, + Name = "Dispatcher", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-1111-1111-1111-111111111114"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Back-office support and billing.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-1111-1111-1111-111111111111"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222221"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Primary creator and deliverable owner.", + IsActive = true, + Name = "Designer", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222222"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Owns timelines, approvals, and client comms.", + IsActive = true, + Name = "Producer", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222223"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Schedules tasks and supports delivery.", + IsActive = true, + Name = "Coordinator", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-2222-2222-2222-222222222224"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Operations and billing support.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-2222-2222-2222-222222222222"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333331"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Client-facing delivery specialist.", + IsActive = true, + Name = "Consultant", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333332"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Owns engagement delivery and quality.", + IsActive = true, + Name = "Lead", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333333"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Plans meetings and follow-ups.", + IsActive = true, + Name = "Coordinator", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-3333-3333-3333-333333333334"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Back-office support and billing.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-3333-3333-3333-333333333333"), + SortOrder = 4 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444441"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Executes diagnostics and repairs.", + IsActive = true, + Name = "Repair Tech", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 1 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444442"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Final testing and release approvals.", + IsActive = true, + Name = "QA", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 2 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444443"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Client intake and status updates.", + IsActive = true, + Name = "Service Advisor", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 3 + }, + new + { + Id = new Guid("2a2b3c4d-4444-4444-4444-444444444444"), + CreatedAt = new DateTime(2026, 3, 23, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Operations and billing support.", + IsActive = true, + Name = "Admin", + PresetId = new Guid("1a2b3c4d-4444-4444-4444-444444444444"), + SortOrder = 4 + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EstimateNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PublicToken") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("PublicTokenExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("SentAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Subtotal") + .HasColumnType("decimal(18,2)"); + + b.Property("TaxTotal") + .HasColumnType("decimal(18,2)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("PublicToken") + .IsUnique(); + + b.HasIndex("OrganizationId", "EstimateNumber") + .IsUnique(); + + b.ToTable("Estimates", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateLineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("PriceBookItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateId"); + + b.HasIndex("PriceBookItemId"); + + b.ToTable("EstimateLineItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateRevisionRequestId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("nvarchar(260)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateRevisionRequestId"); + + b.ToTable("EstimateRevisionAttachments", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationResponseMessage") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("RequestMessage") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("RequestedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ResolvedAt") + .HasColumnType("datetimeoffset"); + + b.Property("ReviewedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RevisionNumber") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("EstimateId", "RevisionNumber") + .IsUnique(); + + b.ToTable("EstimateRevisionRequests", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Day") + .HasColumnType("int"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastSequence") + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EstimateSequence", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpExecutionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AttemptedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Channel") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FailureReason") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("FollowUpRunId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ScheduledFor") + .HasColumnType("datetimeoffset"); + + b.Property("StepOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("WasSent") + .HasColumnType("bit"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpRunId", "StepOrder"); + + b.ToTable("FollowUpExecutionLogs", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FollowUpSequenceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastAttemptAt") + .HasColumnType("datetimeoffset"); + + b.Property("NextStepOrder") + .HasColumnType("int"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SequenceType") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StopReason") + .HasColumnType("int"); + + b.Property("TriggerEntityId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpSequenceId", "OrganizationClientId", "Status"); + + b.HasIndex("OrganizationId", "SequenceType", "TriggerEntityId", "Status"); + + b.ToTable("FollowUpRuns", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultChannel") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SequenceType") + .HasColumnType("int"); + + b.Property("StopOnClientReply") + .HasColumnType("bit"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "SequenceType", "IsActive"); + + b.ToTable("FollowUpSequences", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ChannelOverride") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DelayHours") + .HasColumnType("int"); + + b.Property("FollowUpSequenceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsEscalation") + .HasColumnType("bit"); + + b.Property("MessageTemplate") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("StepOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("FollowUpSequenceId", "StepOrder") + .IsUnique(); + + b.ToTable("FollowUpSteps", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.HelpArticle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ArticleType") + .HasColumnType("int"); + + b.Property("Category") + .HasColumnType("int"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsFeatured") + .HasColumnType("bit"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("PublishedAt") + .HasColumnType("datetimeoffset"); + + b.Property("PublishedBy") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Tags") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ArticleType"); + + b.HasIndex("Category"); + + b.HasIndex("IsPublished"); + + b.ToTable("HelpArticles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CostPerUnit") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("QuantityInStock") + .HasColumnType("int"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("InventoryItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AmountPaid") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("AmountRefunded") + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExternalPaymentId") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceDate") + .HasColumnType("datetime2"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaidAt") + .HasColumnType("datetimeoffset"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.HasIndex("OrderId"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("OrganizationId", "CreatedAt"); + + b.HasIndex("OrganizationId", "Status"); + + b.ToTable("Invoice"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceLineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PriceBookItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("UnitPrice") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.HasIndex("PriceBookItemId"); + + b.ToTable("InvoiceLineItem", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastSequence") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("InvoiceSequence", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Comments") + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EstimateId") + .HasColumnType("uniqueidentifier"); + + b.Property("InvoicingWorkflow") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Latitude") + .HasColumnType("float"); + + b.Property("LifecycleStatus") + .HasColumnType("int"); + + b.Property("Longitude") + .HasColumnType("float"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Title") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EstimateId"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Job", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobRecurrence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DaysOfWeekMask") + .HasColumnType("int"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Duration") + .HasColumnType("time"); + + b.Property("EndDate") + .HasColumnType("datetime2"); + + b.Property("Frequency") + .HasColumnType("int"); + + b.Property("GenerateDaysAhead") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("ScheduleType") + .HasColumnType("int"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("StartTime") + .HasColumnType("time"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId", "IsActive") + .HasDatabaseName("IX_JobRecurrence_JobId_IsActive"); + + b.ToTable("JobRecurrence", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultInvoicingWorkflow") + .HasColumnType("int"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSystem") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.HasIndex("OrganizationTypeId"); + + b.ToTable("JobTemplates", (string)null); + + b.HasData( + new + { + Id = new Guid("3a3b3c3d-0101-0101-0101-010101010101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Full or partial home remodel project.", + IsActive = true, + IsSystem = true, + Name = "Home renovation", + OrganizationTypeId = new Guid("bf489aa6-db19-42df-82bc-c116bd967e7e") + }, + new + { + Id = new Guid("3a3b3c3d-0101-0101-0101-010101010102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "New outdoor living space construction.", + IsActive = true, + IsSystem = true, + Name = "Deck / patio build", + OrganizationTypeId = new Guid("bf489aa6-db19-42df-82bc-c116bd967e7e") + }, + new + { + Id = new Guid("3a3b3c3d-0202-0202-0202-020202020201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Paint walls, ceilings, and trim for interior rooms.", + IsActive = true, + IsSystem = true, + Name = "Interior painting", + OrganizationTypeId = new Guid("393a5b3e-323e-4b76-aa86-0d4683ddcd49") + }, + new + { + Id = new Guid("3a3b3c3d-0202-0202-0202-020202020202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Prep and paint home exterior surfaces.", + IsActive = true, + IsSystem = true, + Name = "Exterior painting", + OrganizationTypeId = new Guid("393a5b3e-323e-4b76-aa86-0d4683ddcd49") + }, + new + { + Id = new Guid("3a3b3c3d-0303-0303-0303-030303030301"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Diagnose and fix leaks, clogs, or faults.", + IsActive = true, + IsSystem = true, + Name = "Plumbing repair", + OrganizationTypeId = new Guid("e01750b0-0d01-4e25-abf7-6efa23509035") + }, + new + { + Id = new Guid("3a3b3c3d-0303-0303-0303-030303030302"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Remove old unit and install replacement.", + IsActive = true, + IsSystem = true, + Name = "Water heater install", + OrganizationTypeId = new Guid("e01750b0-0d01-4e25-abf7-6efa23509035") + }, + new + { + Id = new Guid("3a3b3c3d-0404-0404-0404-040404040401"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Mow, edge, blow, and treat lawn.", + IsActive = true, + IsSystem = true, + Name = "Lawn maintenance", + OrganizationTypeId = new Guid("fbc6accf-0fb1-4908-b449-14f13b826f24") + }, + new + { + Id = new Guid("3a3b3c3d-0404-0404-0404-040404040402"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Design beds, select plants, and install.", + IsActive = true, + IsSystem = true, + Name = "Garden design & install", + OrganizationTypeId = new Guid("fbc6accf-0fb1-4908-b449-14f13b826f24") + }, + new + { + Id = new Guid("3a3b3c3d-0505-0505-0505-050505050501"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Troubleshoot and fix wiring or fixture issues.", + IsActive = true, + IsSystem = true, + Name = "Electrical repair", + OrganizationTypeId = new Guid("9362c957-0f41-4c20-9085-c01e449fdda2") + }, + new + { + Id = new Guid("3a3b3c3d-0505-0505-0505-050505050502"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Upgrade electrical panel for capacity or safety.", + IsActive = true, + IsSystem = true, + Name = "Panel upgrade", + OrganizationTypeId = new Guid("9362c957-0f41-4c20-9085-c01e449fdda2") + }, + new + { + Id = new Guid("3a3b3c3d-0606-0606-0606-060606060601"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Measure, build, and install custom shelves.", + IsActive = true, + IsSystem = true, + Name = "Custom shelving", + OrganizationTypeId = new Guid("8f0d3e93-425b-4a53-b4d2-4c5eb97e490f") + }, + new + { + Id = new Guid("3a3b3c3d-0606-0606-0606-060606060602"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Install baseboards, crown molding, and casings.", + IsActive = true, + IsSystem = true, + Name = "Trim & molding install", + OrganizationTypeId = new Guid("8f0d3e93-425b-4a53-b4d2-4c5eb97e490f") + }, + new + { + Id = new Guid("3a3b3c3d-0707-0707-0707-070707070701"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Seasonal heating/cooling system tune-up.", + IsActive = true, + IsSystem = true, + Name = "HVAC maintenance", + OrganizationTypeId = new Guid("37fc17e8-0a25-4119-9a71-7d160bb9c7b4") + }, + new + { + Id = new Guid("3a3b3c3d-0707-0707-0707-070707070702"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Remove old unit and install new AC system.", + IsActive = true, + IsSystem = true, + Name = "AC install / replacement", + OrganizationTypeId = new Guid("37fc17e8-0a25-4119-9a71-7d160bb9c7b4") + }, + new + { + Id = new Guid("3a3b3c3d-0808-0808-0808-080808080801"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Fell, section, and haul away tree.", + IsActive = true, + IsSystem = true, + Name = "Tree removal", + OrganizationTypeId = new Guid("f64f078f-ecfb-4f3e-8640-236219fcf01e") + }, + new + { + Id = new Guid("3a3b3c3d-0808-0808-0808-080808080802"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Prune branches for health and clearance.", + IsActive = true, + IsSystem = true, + Name = "Tree trimming", + OrganizationTypeId = new Guid("f64f078f-ecfb-4f3e-8640-236219fcf01e") + }, + new + { + Id = new Guid("3a3b3c3d-0909-0909-0909-090909090901"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Interior and exterior spray for common pests.", + IsActive = true, + IsSystem = true, + Name = "General pest treatment", + OrganizationTypeId = new Guid("bf3b9512-8a9c-4a73-9f88-cb914c1573cd") + }, + new + { + Id = new Guid("3a3b3c3d-0909-0909-0909-090909090902"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Full property inspection and treatment plan.", + IsActive = true, + IsSystem = true, + Name = "Termite inspection", + OrganizationTypeId = new Guid("bf3b9512-8a9c-4a73-9f88-cb914c1573cd") + }, + new + { + Id = new Guid("3a3b3c3d-1010-1010-1010-101010101001"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Standard whole-house cleaning visit.", + IsActive = true, + IsSystem = true, + Name = "Full home clean", + OrganizationTypeId = new Guid("1921d982-22f8-4ed5-b4e3-fca82c5767eb") + }, + new + { + Id = new Guid("3a3b3c3d-1010-1010-1010-101010101002"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Thorough clean for tenant turnover.", + IsActive = true, + IsSystem = true, + Name = "Move-out deep clean", + OrganizationTypeId = new Guid("1921d982-22f8-4ed5-b4e3-fca82c5767eb") + }, + new + { + Id = new Guid("3a3b3c3d-1111-1111-1111-111111111101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Remove all unwanted items from property.", + IsActive = true, + IsSystem = true, + Name = "Full property cleanout", + OrganizationTypeId = new Guid("408d2185-53b9-493d-8713-938114de90f5") + }, + new + { + Id = new Guid("3a3b3c3d-1111-1111-1111-111111111102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Clear and haul garage contents.", + IsActive = true, + IsSystem = true, + Name = "Garage cleanout", + OrganizationTypeId = new Guid("408d2185-53b9-493d-8713-938114de90f5") + }, + new + { + Id = new Guid("3a3b3c3d-1212-1212-1212-121212121201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Complete interior and exterior detail.", + IsActive = true, + IsSystem = true, + Name = "Full detail", + OrganizationTypeId = new Guid("33341b2d-957f-4efb-94f7-3a015ae1a718") + }, + new + { + Id = new Guid("3a3b3c3d-1212-1212-1212-121212121202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Deep clean seats, dash, carpet, and glass.", + IsActive = true, + IsSystem = true, + Name = "Interior only", + OrganizationTypeId = new Guid("33341b2d-957f-4efb-94f7-3a015ae1a718") + }, + new + { + Id = new Guid("3a3b3c3d-1313-1313-1313-131313131301"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Install and configure router, switches, and cabling.", + IsActive = true, + IsSystem = true, + Name = "Network setup", + OrganizationTypeId = new Guid("30530a32-a151-436d-a050-613eac4c22d5") + }, + new + { + Id = new Guid("3a3b3c3d-1313-1313-1313-131313131302"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Set up desktops, laptops, and peripherals.", + IsActive = true, + IsSystem = true, + Name = "Workstation deployment", + OrganizationTypeId = new Guid("30530a32-a151-436d-a050-613eac4c22d5") + }, + new + { + Id = new Guid("3a3b3c3d-1414-1414-1414-141414141401"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Fix miscellaneous issues around the home.", + IsActive = true, + IsSystem = true, + Name = "General repair", + OrganizationTypeId = new Guid("0f32e14a-5f70-45af-a647-04e59ad52e58") + }, + new + { + Id = new Guid("3a3b3c3d-1414-1414-1414-141414141402"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 1, + Description = "Mount and connect lights, fans, or hardware.", + IsActive = true, + IsSystem = true, + Name = "Fixture install", + OrganizationTypeId = new Guid("0f32e14a-5f70-45af-a647-04e59ad52e58") + }, + new + { + Id = new Guid("3a3b3c3d-1515-1515-1515-151515151501"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Measure, prep subfloor, and install hardwood.", + IsActive = true, + IsSystem = true, + Name = "Hardwood install", + OrganizationTypeId = new Guid("09786eab-d69f-45bf-bcec-5f368bd60be7") + }, + new + { + Id = new Guid("3a3b3c3d-1515-1515-1515-151515151502"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + DefaultInvoicingWorkflow = 0, + Description = "Lay tile with proper spacing and grout.", + IsActive = true, + IsSystem = true, + Name = "Tile install", + OrganizationTypeId = new Guid("09786eab-d69f-45bf-bcec-5f368bd60be7") + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplateItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(240) + .HasColumnType("nvarchar(240)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("TemplateId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TemplateId"); + + b.ToTable("JobTemplateItems", (string)null); + + b.HasData( + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Walk property and scope of work.", + IsActive = true, + Name = "Site assessment", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove old materials and prep surfaces.", + IsActive = true, + Name = "Demo & prep", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Construction, paint, and final touches.", + IsActive = true, + Name = "Build & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010101") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Draft plan and pull permits.", + IsActive = true, + Name = "Design & permits", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set footings, frame, and deck boards.", + IsActive = true, + Name = "Frame & build", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0101-0101-0101-010101010203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Stain/seal and final walkthrough.", + IsActive = true, + Name = "Finish & inspect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0101-0101-0101-010101010102") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm colors, tape, and cover surfaces.", + IsActive = true, + Name = "Color consult & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Apply primer and two coats.", + IsActive = true, + Name = "Prime & paint", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Detail edges and remove coverings.", + IsActive = true, + Name = "Touch-up & clean", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020201") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean and prep exterior surfaces.", + IsActive = true, + Name = "Power wash & scrape", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Coat siding, fascia, and trim.", + IsActive = true, + Name = "Prime & paint", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0202-0202-0202-020202020203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check coverage and clean site.", + IsActive = true, + Name = "Final inspection", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0202-0202-0202-020202020202") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Locate leak, blockage, or fault.", + IsActive = true, + Name = "Diagnose issue", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fix or replace the faulty component.", + IsActive = true, + Name = "Perform repair", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Run water, check pressure, tidy area.", + IsActive = true, + Name = "Test & clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030301") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Shut off supply and drain tank.", + IsActive = true, + Name = "Disconnect old unit", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Position, connect plumbing and power.", + IsActive = true, + Name = "Install new unit", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0303-0303-0303-030303030203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check for leaks and set temperature.", + IsActive = true, + Name = "Test & verify", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0303-0303-0303-030303030302") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut grass and trim borders.", + IsActive = true, + Name = "Mow & edge", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clear clippings from walks and drives.", + IsActive = true, + Name = "Blow & clean", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Apply treatment as needed.", + IsActive = true, + Name = "Treat & fertilize", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040401") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Plan beds, paths, and plant selection.", + IsActive = true, + Name = "Design layout", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Amend soil and install plants.", + IsActive = true, + Name = "Prep & plant", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0404-0404-0404-040404040203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Top-dress and initial watering.", + IsActive = true, + Name = "Mulch & water", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0404-0404-0404-040404040402") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Test circuits and locate issue.", + IsActive = true, + Name = "Diagnose fault", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Replace or repair faulty components.", + IsActive = true, + Name = "Repair wiring", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm power and safety.", + IsActive = true, + Name = "Test & verify", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050501") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Kill main and label circuits.", + IsActive = true, + Name = "Shut down & disconnect", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mount new box and reconnect breakers.", + IsActive = true, + Name = "Swap panel", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0505-0505-0505-050505050203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Restore power and run tests.", + IsActive = true, + Name = "Energize & inspect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0505-0505-0505-050505050502") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Take dimensions and plan layout.", + IsActive = true, + Name = "Measure & design", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Build shelving units.", + IsActive = true, + Name = "Cut & assemble", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mount, sand, and apply finish.", + IsActive = true, + Name = "Install & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060601") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Measure runs and miter cut pieces.", + IsActive = true, + Name = "Measure & cut", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Attach molding and set nails.", + IsActive = true, + Name = "Nail & set", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0606-0606-0606-060606060203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fill gaps, prime, and paint.", + IsActive = true, + Name = "Caulk & paint", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0606-0606-0606-060606060602") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check airflow and swap filter.", + IsActive = true, + Name = "Inspect & replace filter", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove buildup from outdoor unit.", + IsActive = true, + Name = "Clean condenser coils", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify settings and calibration.", + IsActive = true, + Name = "Test thermostat", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070701") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Disconnect and haul away.", + IsActive = true, + Name = "Remove old unit", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set unit, connect lines and electric.", + IsActive = true, + Name = "Install new system", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0707-0707-0707-070707070203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Add refrigerant and run cycles.", + IsActive = true, + Name = "Charge & test", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0707-0707-0707-070707070702") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Evaluate drop zone and rigging.", + IsActive = true, + Name = "Assess & plan", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut tree and process limbs.", + IsActive = true, + Name = "Fell & section", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove debris and rake site.", + IsActive = true, + Name = "Haul & clean", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080801") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Identify dead or problem branches.", + IsActive = true, + Name = "Inspect canopy", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Cut for shape, clearance, and health.", + IsActive = true, + Name = "Prune branches", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0808-0808-0808-080808080203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Chip or haul brush.", + IsActive = true, + Name = "Clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0808-0808-0808-080808080802") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Identify pest activity and entry points.", + IsActive = true, + Name = "Inspect property", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Spray interior and exterior perimeter.", + IsActive = true, + Name = "Apply treatment", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Log findings and next visit.", + IsActive = true, + Name = "Document & recommend", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090901") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Check foundation, crawl, and attic.", + IsActive = true, + Name = "Full property scan", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Tap and test for damage.", + IsActive = true, + Name = "Probe suspect areas", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-0909-0909-0909-090909090203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Findings and treatment estimate.", + IsActive = true, + Name = "Deliver report", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-0909-0909-0909-090909090902") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Assess rooms and special requests.", + IsActive = true, + Name = "Walkthrough", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Scrub fixtures, counters, appliances.", + IsActive = true, + Name = "Deep clean kitchen & baths", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "All hard and carpeted surfaces.", + IsActive = true, + Name = "Vacuum & mop", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101001") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clear remaining items, dust surfaces.", + IsActive = true, + Name = "Empty & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Walls, baseboards, inside cabinets.", + IsActive = true, + Name = "Scrub all rooms", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1010-1010-1010-101010100203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify quality before handoff.", + IsActive = true, + Name = "Final walkthrough", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1010-1010-1010-101010101002") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Tag items and plan truck loads.", + IsActive = true, + Name = "Walk & inventory", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Remove items to truck.", + IsActive = true, + Name = "Load & haul", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean site, dump or donate.", + IsActive = true, + Name = "Sweep & dispose", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111101") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Separate keep, donate, and trash.", + IsActive = true, + Name = "Sort items", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Move discards to truck.", + IsActive = true, + Name = "Load out", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1111-1111-1111-111111110203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Broom clean and organize keepers.", + IsActive = true, + Name = "Sweep garage", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1111-1111-1111-111111111102") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Hand wash, clay bar, and dry.", + IsActive = true, + Name = "Exterior wash & clay", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Vacuum, shampoo, and condition.", + IsActive = true, + Name = "Interior deep clean", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Compound, wax, and dress tires.", + IsActive = true, + Name = "Polish & protect", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121201") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "All seats, dash, and crevices.", + IsActive = true, + Name = "Vacuum & dust", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Extract and clean fabric or leather.", + IsActive = true, + Name = "Shampoo upholstery", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1212-1212-1212-121212120203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Clean interior glass and surfaces.", + IsActive = true, + Name = "Glass & final wipe", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1212-1212-1212-121212121202") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Map runs and plan equipment placement.", + IsActive = true, + Name = "Site survey", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Pull cable and install hardware.", + IsActive = true, + Name = "Run cable & mount", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Set up network and verify connectivity.", + IsActive = true, + Name = "Configure & test", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131301") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Prep hardware and install OS/software.", + IsActive = true, + Name = "Unbox & image", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Place at desk and join network.", + IsActive = true, + Name = "Deploy & connect", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1313-1313-1313-131313130203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Verify apps and orient user.", + IsActive = true, + Name = "User walkthrough", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1313-1313-1313-131313131302") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Inspect problem area and plan fix.", + IsActive = true, + Name = "Assess issue", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fix or replace components.", + IsActive = true, + Name = "Repair", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Test fix and tidy work area.", + IsActive = true, + Name = "Verify & clean up", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141401") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Mark placement and run wiring if needed.", + IsActive = true, + Name = "Prep location", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Secure and connect.", + IsActive = true, + Name = "Mount fixture", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1414-1414-1414-141414140203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Confirm operation and patch holes.", + IsActive = true, + Name = "Test & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1414-1414-1414-141414141402") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150101"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Level, clean, and lay moisture barrier.", + IsActive = true, + Name = "Prep subfloor", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150102"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lay and nail or click into place.", + IsActive = true, + Name = "Install planks", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150103"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Install transitions and clean.", + IsActive = true, + Name = "Trim & finish", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151501") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150201"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Dry-fit pattern and mix thinset.", + IsActive = true, + Name = "Layout & prep", + SortOrder = 1, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150202"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Lay tiles with spacers.", + IsActive = true, + Name = "Set tile", + SortOrder = 2, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }, + new + { + Id = new Guid("4a4b4c4d-1515-1515-1515-151515150203"), + CreatedAt = new DateTime(2026, 4, 8, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Fill joints, clean haze, and seal.", + IsActive = true, + Name = "Grout & seal", + SortOrder = 3, + TemplateId = new Guid("3a3b3c3d-1515-1515-1515-151515151502") + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTracking", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmployeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Latitude") + .HasColumnType("decimal(9,6)"); + + b.Property("Longitude") + .HasColumnType("decimal(9,6)"); + + b.Property("RecordedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("JobId"); + + b.ToTable("JobTracking"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobId") + .HasColumnType("uniqueidentifier"); + + b.Property("Message") + .HasMaxLength(4000) + .HasColumnType("nvarchar(4000)"); + + b.Property("OccurredAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Type") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobId", "OccurredAt"); + + b.ToTable("JobUpdates", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdateAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FileData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(260) + .HasColumnType("nvarchar(260)"); + + b.Property("FileSizeBytes") + .HasColumnType("bigint"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("JobUpdateId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("JobUpdateId"); + + b.ToTable("JobUpdateAttachments", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AttachmentUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("Content") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConversationId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExternalSenderName") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("ExternalSenderPhone") + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("ExternalSenderType") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsRead") + .HasColumnType("bit"); + + b.Property("SenderId") + .HasColumnType("uniqueidentifier"); + + b.Property("SentAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ConversationId"); + + b.HasIndex("SenderId"); + + b.ToTable("Message", "messaging"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Notes") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OrderDate") + .HasColumnType("datetime2"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TotalAmount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.ToTable("Order", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("Address2") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactFirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactLastName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultTaxRate") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("EnableTax") + .HasColumnType("bit"); + + b.Property("HasFreeAccount") + .HasColumnType("bit"); + + b.Property("IndustryKey") + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsSquareConnected") + .HasColumnType("bit"); + + b.Property("IsStripeConnected") + .HasColumnType("bit"); + + b.Property("OnBoardingComplete") + .HasColumnType("bit"); + + b.Property("OnboardingPresetAppliedAt") + .HasColumnType("datetimeoffset"); + + b.Property("OnboardingPresetKey") + .HasColumnType("nvarchar(max)"); + + b.Property("OnboardingTrack") + .HasColumnType("nvarchar(max)"); + + b.Property("OnboardingTrackSelectedAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationTypeId") + .HasColumnType("uniqueidentifier"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("SquareMerchantId") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("StripeConnectAccountId") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionExpiresAt") + .HasColumnType("datetime2"); + + b.Property("SubscriptionPlanName") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationTypeId"); + + b.ToTable("Organization", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationBranding", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("BusinessName") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("FooterNote") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PrimaryColor") + .HasColumnType("nvarchar(max)"); + + b.Property("SecondaryColor") + .HasColumnType("nvarchar(max)"); + + b.Property("Tagline") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationBranding", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Address1") + .HasColumnType("nvarchar(max)"); + + b.Property("Address2") + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(450)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("State") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("ZipCode") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "EmailAddress") + .IsUnique() + .HasFilter("[EmailAddress] IS NOT NULL AND [IsActive] = 1"); + + b.ToTable("OrganizationClient", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClientPortalSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .IsRequired() + .HasMaxLength(320) + .HasColumnType("nvarchar(320)"); + + b.Property("ExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("RedeemedAt") + .HasColumnType("datetimeoffset"); + + b.Property("TokenHash") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationClientId"); + + b.HasIndex("TokenHash") + .IsUnique(); + + b.ToTable("OrganizationClientPortalSession"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationInvoicingSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultWorkflow") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("DepositPercentage") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("DepositRequired") + .HasColumnType("bit"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationInvoicingSettings"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationOnboardingStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CompletedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "StepName") + .IsUnique(); + + b.ToTable("OrganizationOnboardingSteps", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationScheduleSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AutoNotifyReschedule") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("DefaultWindowMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(120); + + b.Property("EnforceTravelBuffer") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("TravelBufferMinutes") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(20); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId") + .IsUnique(); + + b.ToTable("OrganizationScheduleSettings"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("ServiceName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("OrganizationService", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("TypeName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("OrganizationType", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationWorkflowStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("StatusKey") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Category", "StatusKey") + .IsUnique(); + + b.ToTable("OrganizationWorkflowStatus"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PaymentHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AmountPaid") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerId") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EntityId") + .HasColumnType("uniqueidentifier"); + + b.Property("EntityType") + .HasColumnType("int"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentProvider") + .HasColumnType("int"); + + b.Property("RawEventJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeInvoiceId") + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntentId") + .HasColumnType("nvarchar(max)"); + + b.Property("SubscriptionId") + .HasColumnType("nvarchar(max)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.HasIndex("EntityId", "PaidAt"); + + b.ToTable("PaymentHistory"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("PriceBookCategories", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("Cost") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("InventoryItemId") + .HasColumnType("uniqueidentifier"); + + b.Property("InventoryUnitsPerSale") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,4)") + .HasDefaultValue(1.0m); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsTaxable") + .HasColumnType("bit"); + + b.Property("ItemType") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PartNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("PricePerUnit") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("InventoryItemId"); + + b.HasIndex("OrganizationId", "Name") + .IsUnique(); + + b.ToTable("PriceBookItems", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SecurityAlert", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("nvarchar(80)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("nvarchar(1024)"); + + b.Property("DetailsJson") + .HasColumnType("nvarchar(max)"); + + b.Property("EvidenceCount") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RuleKey") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Severity") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("WindowEndUtc") + .HasColumnType("datetime2"); + + b.Property("WindowStartUtc") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("RuleKey", "Status", "CreatedAt"); + + b.ToTable("SecurityAlert", "security"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ShortLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessCount") + .HasColumnType("int"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("nvarchar(16)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastAccessedAt") + .HasColumnType("datetime2"); + + b.Property("TargetUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("nvarchar(2048)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique() + .HasFilter("[IsActive] = 1"); + + b.HasIndex("CreatedAt"); + + b.ToTable("ShortLink", "notifications"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SubscriptionRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CanceledAt") + .HasColumnType("datetime2"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("PaymentProfileId") + .HasColumnType("uniqueidentifier"); + + b.Property("PlanName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Provider") + .HasColumnType("int"); + + b.Property("ProviderPriceId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderSubscriptionId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("StartDate") + .HasColumnType("datetime2"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("PaymentProfileId"); + + b.ToTable("SubscriptionRecord", "payment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubInvite", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(12) + .HasColumnType("nvarchar(12)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("ExpiresAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("RedeemedAt") + .HasColumnType("datetimeoffset"); + + b.Property("RedeemedByUid") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.ToTable("SupportHubInvites"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AgentName") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("nvarchar(120)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("EndedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("StartedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SupportHubSessions"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubTicket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastActivityAt") + .HasColumnType("datetimeoffset"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Summary") + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(160) + .HasColumnType("nvarchar(160)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("OrganizationId"); + + b.ToTable("SupportHubTickets"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SystemRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles", (string)null); + + b.HasData( + new + { + Id = new Guid("e88fbbe6-8bdf-4aca-b941-912785a94f0b"), + Name = "OrganizationAdmin" + }, + new + { + Id = new Guid("079e4277-0eb2-4222-82e4-5a751ede48f6"), + Name = "OrganizationEmployee" + }, + new + { + Id = new Guid("3da14c58-562a-437a-a2a6-47706b40eb70"), + Name = "OrganizationClient" + }, + new + { + Id = new Guid("5bc0d325-a915-4e17-8184-428ee533cf89"), + Name = "KatharixAdmin" + }, + new + { + Id = new Guid("92193eb2-dba0-433c-814e-9fca95bde016"), + Name = "KatharixEmployee" + }, + new + { + Id = new Guid("dfe36ebc-bfb5-4583-b68e-59be8ba60fa9"), + Name = "SuperAdmin" + }); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("FirebaseUid") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastName") + .HasColumnType("nvarchar(max)"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PreferredLanguage") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("OrganizationId"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.UserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("Assignments") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentAssignee", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany("AssignmentAssignees") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Employee", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentHistory", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany() + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.AssignmentOrder", b => + { + b.HasOne("JobFlow.Domain.Models.Assignment", "Assignment") + .WithMany("AssignmentOrders") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Order", "Order") + .WithMany("AssignmentOrders") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJobError", b => + { + b.HasOne("JobFlow.Domain.Models.ClientImportJob", "ClientImportJob") + .WithMany("Errors") + .HasForeignKey("ClientImportJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ClientImportJob"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadRow", b => + { + b.HasOne("JobFlow.Domain.Models.ClientImportUploadSession", "Session") + .WithMany("Rows") + .HasForeignKey("ClientImportUploadSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Session"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ConversationParticipant", b => + { + b.HasOne("JobFlow.Domain.Models.Conversation", "Conversation") + .WithMany("Participants") + .HasForeignKey("ConversationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Conversation"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.CustomerPaymentProfile", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", null) + .WithMany("PaymentProfiles") + .HasForeignKey("OrganizationClientId"); + + b.HasOne("JobFlow.Domain.Models.Organization", null) + .WithMany("PaymentProfiles") + .HasForeignKey("OrganizationId"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.DataExportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Employee", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("Employees") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.EmployeeRole", "Role") + .WithMany("Employees") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany("Employees") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Organization"); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJobError", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeImportJob", "EmployeeImportJob") + .WithMany("Errors") + .HasForeignKey("EmployeeImportJobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EmployeeImportJob"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadRow", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeImportUploadSession", "Session") + .WithMany("Rows") + .HasForeignKey("EmployeeImportUploadSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Session"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeInvite", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.EmployeeRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("EmployeeRoles") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePresetItem", b => + { + b.HasOne("JobFlow.Domain.Models.EmployeeRolePreset", "Preset") + .WithMany("Items") + .HasForeignKey("PresetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Preset"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateLineItem", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany("LineItems") + .HasForeignKey("EstimateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.PriceBookItem", "PriceBookItem") + .WithMany() + .HasForeignKey("PriceBookItemId"); + + b.Navigation("Estimate"); + + b.Navigation("PriceBookItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionAttachment", b => + { + b.HasOne("JobFlow.Domain.Models.EstimateRevisionRequest", "RevisionRequest") + .WithMany("Attachments") + .HasForeignKey("EstimateRevisionRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RevisionRequest"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany("RevisionRequests") + .HasForeignKey("EstimateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Estimate"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpExecutionLog", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpRun", "Run") + .WithMany("ExecutionLogs") + .HasForeignKey("FollowUpRunId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Run"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpSequence", "Sequence") + .WithMany() + .HasForeignKey("FollowUpSequenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sequence"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpStep", b => + { + b.HasOne("JobFlow.Domain.Models.FollowUpSequence", "Sequence") + .WithMany("Steps") + .HasForeignKey("FollowUpSequenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Sequence"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId"); + + b.HasOne("JobFlow.Domain.Models.Order", "Order") + .WithMany("Invoices") + .HasForeignKey("OrderId"); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + + b.Navigation("Order"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.InvoiceLineItem", b => + { + b.HasOne("JobFlow.Domain.Models.Invoice", "Invoice") + .WithMany("LineItems") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.PriceBookItem", "PriceBookItem") + .WithMany() + .HasForeignKey("PriceBookItemId"); + + b.Navigation("Invoice"); + + b.Navigation("PriceBookItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.HasOne("JobFlow.Domain.Models.Estimate", "Estimate") + .WithMany() + .HasForeignKey("EstimateId"); + + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany("Jobs") + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Estimate"); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobRecurrence", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany() + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("JobFlow.Domain.Models.OrganizationType", "OrganizationType") + .WithMany() + .HasForeignKey("OrganizationTypeId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Organization"); + + b.Navigation("OrganizationType"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplateItem", b => + { + b.HasOne("JobFlow.Domain.Models.JobTemplate", "Template") + .WithMany("Items") + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Template"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTracking", b => + { + b.HasOne("JobFlow.Domain.Models.User", "Employee") + .WithMany() + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("JobTrackings") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.HasOne("JobFlow.Domain.Models.Job", "Job") + .WithMany("JobUpdates") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Job"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdateAttachment", b => + { + b.HasOne("JobFlow.Domain.Models.JobUpdate", "JobUpdate") + .WithMany("Attachments") + .HasForeignKey("JobUpdateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("JobUpdate"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Message", b => + { + b.HasOne("JobFlow.Domain.Models.Conversation", "Conversation") + .WithMany("Messages") + .HasForeignKey("ConversationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "Sender") + .WithMany() + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Conversation"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationType", "OrganizationType") + .WithMany() + .HasForeignKey("OrganizationTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OrganizationType"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationBranding", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClientPortalSession", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "OrganizationClient") + .WithMany() + .HasForeignKey("OrganizationClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("OrganizationClient"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationOnboardingStep", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany("OnboardingSteps") + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationService", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PaymentHistory", b => + { + b.HasOne("JobFlow.Domain.Models.Invoice", "Invoice") + .WithMany("Payments") + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookItem", b => + { + b.HasOne("JobFlow.Domain.Models.PriceBookCategory", "Category") + .WithMany("Items") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("JobFlow.Domain.Models.InventoryItem", "InventoryItem") + .WithMany() + .HasForeignKey("InventoryItemId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Category"); + + b.Navigation("InventoryItem"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SubscriptionRecord", b => + { + b.HasOne("JobFlow.Domain.Models.CustomerPaymentProfile", "PaymentProfile") + .WithMany() + .HasForeignKey("PaymentProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PaymentProfile"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubSession", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SupportHubTicket", b => + { + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.HasOne("JobFlow.Domain.Models.OrganizationClient", "Client") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("JobFlow.Domain.Models.Organization", "Organization") + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("Organization"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.UserRole", b => + { + b.HasOne("JobFlow.Domain.Models.SystemRole", "Role") + .WithMany("UserRoles") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("JobFlow.Domain.Models.User", "User") + .WithMany("UserRoles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Assignment", b => + { + b.Navigation("AssignmentAssignees"); + + b.Navigation("AssignmentOrders"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportJob", b => + { + b.Navigation("Errors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.ClientImportUploadSession", b => + { + b.Navigation("Rows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Conversation", b => + { + b.Navigation("Messages"); + + b.Navigation("Participants"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportJob", b => + { + b.Navigation("Errors"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeImportUploadSession", b => + { + b.Navigation("Rows"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRole", b => + { + b.Navigation("Employees"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EmployeeRolePreset", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Estimate", b => + { + b.Navigation("LineItems"); + + b.Navigation("RevisionRequests"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.EstimateRevisionRequest", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpRun", b => + { + b.Navigation("ExecutionLogs"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpSequence", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Invoice", b => + { + b.Navigation("LineItems"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Job", b => + { + b.Navigation("Assignments"); + + b.Navigation("JobTrackings"); + + b.Navigation("JobUpdates"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobTemplate", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.JobUpdate", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Order", b => + { + b.Navigation("AssignmentOrders"); + + b.Navigation("Invoices"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.Organization", b => + { + b.Navigation("EmployeeRoles"); + + b.Navigation("Employees"); + + b.Navigation("OnboardingSteps"); + + b.Navigation("PaymentProfiles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.OrganizationClient", b => + { + b.Navigation("Jobs"); + + b.Navigation("PaymentProfiles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.PriceBookCategory", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.SystemRole", b => + { + b.Navigation("UserRoles"); + }); + + modelBuilder.Entity("JobFlow.Domain.Models.User", b => + { + b.Navigation("Employees"); + + b.Navigation("UserRoles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.cs b/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.cs new file mode 100644 index 0000000..7f0d5d9 --- /dev/null +++ b/JobFlow.Infrastructure.Persistence/Migrations/20260414024516_AppHardeningSchemaChanges.cs @@ -0,0 +1,60 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace JobFlow.Infrastructure.Persistence.Migrations +{ + /// + public partial class AppHardeningSchemaChanges : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Quantity", + table: "InvoiceLineItem", + type: "decimal(18,2)", + nullable: false, + oldClrType: typeof(int), + oldType: "int"); + + migrationBuilder.CreateTable( + name: "EstimateSequence", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + OrganizationId = table.Column(type: "uniqueidentifier", nullable: false), + Year = table.Column(type: "int", nullable: false), + Month = table.Column(type: "int", nullable: false), + Day = table.Column(type: "int", nullable: false), + LastSequence = table.Column(type: "int", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: true), + UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + UpdatedAt = table.Column(type: "datetime2", nullable: true), + IsActive = table.Column(type: "bit", nullable: false), + DeactivatedAtUtc = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_EstimateSequence", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "EstimateSequence"); + + migrationBuilder.AlterColumn( + name: "Quantity", + table: "InvoiceLineItem", + type: "int", + nullable: false, + oldClrType: typeof(decimal), + oldType: "decimal(18,2)"); + } + } +} diff --git a/JobFlow.Infrastructure.Persistence/Migrations/JobFlowDbContextModelSnapshot.cs b/JobFlow.Infrastructure.Persistence/Migrations/JobFlowDbContextModelSnapshot.cs index 41672e6..56320b0 100644 --- a/JobFlow.Infrastructure.Persistence/Migrations/JobFlowDbContextModelSnapshot.cs +++ b/JobFlow.Infrastructure.Persistence/Migrations/JobFlowDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.3") + .HasAnnotation("ProductVersion", "10.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -1690,6 +1690,50 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("EstimateRevisionRequests", (string)null); }); + modelBuilder.Entity("JobFlow.Domain.Models.EstimateSequence", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Day") + .HasColumnType("int"); + + b.Property("DeactivatedAtUtc") + .HasColumnType("datetime2"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("LastSequence") + .HasColumnType("int"); + + b.Property("Month") + .HasColumnType("int"); + + b.Property("OrganizationId") + .HasColumnType("uniqueidentifier"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Year") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("EstimateSequence", (string)null); + }); + modelBuilder.Entity("JobFlow.Domain.Models.FollowUpExecutionLog", b => { b.Property("Id") @@ -2056,6 +2100,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasPrecision(18, 2) .HasColumnType("decimal(18,2)"); + b.Property("AmountRefunded") + .HasColumnType("decimal(18,2)"); + b.Property("CreatedAt") .HasColumnType("datetime2"); @@ -2159,8 +2206,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PriceBookItemId") .HasColumnType("uniqueidentifier"); - b.Property("Quantity") - .HasColumnType("int"); + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); b.Property("UnitPrice") .HasPrecision(18, 2) diff --git a/JobFlow.Infrastructure/PaymentGateways/Stripe/StripePaymentProcessor.cs b/JobFlow.Infrastructure/PaymentGateways/Stripe/StripePaymentProcessor.cs index 9953481..0529490 100644 --- a/JobFlow.Infrastructure/PaymentGateways/Stripe/StripePaymentProcessor.cs +++ b/JobFlow.Infrastructure/PaymentGateways/Stripe/StripePaymentProcessor.cs @@ -133,7 +133,7 @@ public async Task RefundPaymentAsync(PaymentRefundReques throw new InvalidOperationException("Provider payment id is required."); var refundService = new RefundService(); - var refund = await refundService.CreateAsync(new RefundCreateOptions + var options = new RefundCreateOptions { PaymentIntent = request.ProviderPaymentId, Amount = request.Amount.ToCents(), @@ -143,7 +143,15 @@ public async Task RefundPaymentAsync(PaymentRefundReques "fraudulent" => "fraudulent", _ => "requested_by_customer" } - }); + }; + + RequestOptions? requestOptions = null; + if (!string.IsNullOrWhiteSpace(request.ConnectedAccountId)) + { + requestOptions = new RequestOptions { StripeAccount = request.ConnectedAccountId }; + } + + var refund = await refundService.CreateAsync(options, requestOptions); return new PaymentOperationResult {