diff --git a/.github/workflows/cypress-dev.yml b/.github/workflows/cypress-dev.yml index b010ab5519..da7eff10bf 100644 --- a/.github/workflows/cypress-dev.yml +++ b/.github/workflows/cypress-dev.yml @@ -31,4 +31,5 @@ jobs: env_name: dev cypress_config_key: CYPRESS_CONFIG_DEV base_url: ${{ inputs.base_url || '' }} + git_ref: ${{ github.event.workflow_run.head_sha || github.sha }} secrets: inherit diff --git a/.github/workflows/cypress-e2e-runner.yml b/.github/workflows/cypress-e2e-runner.yml index 6dfab257b4..762d55d7c8 100644 --- a/.github/workflows/cypress-e2e-runner.yml +++ b/.github/workflows/cypress-e2e-runner.yml @@ -33,6 +33,10 @@ on: required: false type: string default: "" + git_ref: + description: "Branch/tag/SHA of bcgov/Unity to check out — must be the triggering commit, not github.sha (workflow_run events set github.sha to the default branch, not the triggering branch)" + required: true + type: string permissions: contents: read @@ -69,7 +73,7 @@ jobs: JOB_REF=$(oc process unity-cypress-job \ -p ENV=${{ inputs.env_name }} \ -p CYPRESS_CONFIG_KEY=${{ inputs.cypress_config_key }} \ - -p GIT_REF=$GITHUB_SHA \ + -p GIT_REF="${{ inputs.git_ref }}" \ -p GIT_TOKEN=$GH_TOKEN \ -p BASE_URL="${{ inputs.base_url }}" \ -n $TOOLS_NAMESPACE \ diff --git a/.github/workflows/cypress-prod.yml b/.github/workflows/cypress-prod.yml index a9e9fcde95..117db6f7fd 100644 --- a/.github/workflows/cypress-prod.yml +++ b/.github/workflows/cypress-prod.yml @@ -16,4 +16,5 @@ jobs: with: env_name: prod cypress_config_key: CYPRESS_CONFIG_PROD + git_ref: ${{ github.event.workflow_run.head_sha || github.sha }} secrets: inherit diff --git a/.github/workflows/cypress-test.yml b/.github/workflows/cypress-test.yml index 9a907f33d9..f49d93145d 100644 --- a/.github/workflows/cypress-test.yml +++ b/.github/workflows/cypress-test.yml @@ -21,4 +21,5 @@ jobs: with: env_name: test cypress_config_key: CYPRESS_CONFIG_TEST + git_ref: ${{ github.event.workflow_run.head_sha || github.sha }} secrets: inherit diff --git a/.github/workflows/cypress-uat.yml b/.github/workflows/cypress-uat.yml index a33f814e2f..503f3e68c8 100644 --- a/.github/workflows/cypress-uat.yml +++ b/.github/workflows/cypress-uat.yml @@ -24,4 +24,5 @@ jobs: with: env_name: uat cypress_config_key: CYPRESS_CONFIG_UAT + git_ref: ${{ github.event.workflow_run.head_sha || github.sha }} secrets: inherit diff --git a/.github/workflows/docker-build-dev.yml b/.github/workflows/docker-build-dev.yml index d9cab254ff..e18876adb8 100644 --- a/.github/workflows/docker-build-dev.yml +++ b/.github/workflows/docker-build-dev.yml @@ -73,7 +73,7 @@ jobs: - name: Get short commitId id: get_commit run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT - name: List merged branch id: get_branch run: | diff --git a/.github/workflows/docker-build-dev2.yml b/.github/workflows/docker-build-dev2.yml index 5a9cad5428..759adeb628 100644 --- a/.github/workflows/docker-build-dev2.yml +++ b/.github/workflows/docker-build-dev2.yml @@ -60,7 +60,7 @@ jobs: - name: Get short commitId id: get_commit run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT outputs: SHA_SHORT: ${{steps.get_commit.outputs.SHA_SHORT}} Build: diff --git a/.github/workflows/docker-build-main.yml b/.github/workflows/docker-build-main.yml index f967c20619..6fb7d075e6 100644 --- a/.github/workflows/docker-build-main.yml +++ b/.github/workflows/docker-build-main.yml @@ -71,7 +71,7 @@ jobs: - name: Get short commitId id: get_commit run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT - name: List merged branch id: get_branch run: | diff --git a/.github/workflows/docker-build-test.yml b/.github/workflows/docker-build-test.yml index 9ea19ba14e..d69decfce1 100644 --- a/.github/workflows/docker-build-test.yml +++ b/.github/workflows/docker-build-test.yml @@ -73,7 +73,7 @@ jobs: - name: Get short commitId id: get_commit run: | - echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "SHA_SHORT=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT - name: List merged branch id: get_branch run: | diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantApplications/GrantApplicationDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantApplications/GrantApplicationDto.cs index 4861a89cb1..8ff13794d6 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantApplications/GrantApplicationDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantApplications/GrantApplicationDto.cs @@ -89,6 +89,5 @@ public class GrantApplicationDto : AuditedEntityDto public string? AIAnalysis { get; set; } public Guid? DefaultSiteId { get; set; } public ApplicationAnalysisResponse? AIAnalysisData { get; set; } - public string? AIScoresheetAnswers { get; set; } public bool EligibleForRenewal { get; set; } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs index 4a46d2b10f..0b207cfdce 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs @@ -3,24 +3,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; using Unity.AI.Permissions; -using Unity.Flex; -using Unity.Flex.Scoresheets; -using Unity.Flex.Scoresheets.Enums; -using Unity.Flex.Scoresheets.Events; -using Unity.Flex.Worksheets.Definitions; using Unity.GrantManager.Applications; -using Unity.GrantManager.Comments; -using Unity.GrantManager.Exceptions; using Unity.GrantManager.Workflow; using Unity.Modules.Shared; using Volo.Abp; using Volo.Abp.Application.Services; using Volo.Abp.DependencyInjection; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.EventBus.Local; using Volo.Abp.Features; using Volo.Abp.Identity.Integration; using Volo.Abp.Users; @@ -36,35 +26,24 @@ public class AssessmentAppService : ApplicationService, IAssessmentAppService private readonly IAssessmentRepository _assessmentRepository; private readonly AssessmentManager _assessmentManager; private readonly IApplicationRepository _applicationRepository; - private readonly IIdentityUserIntegrationService _userLookupProvider; - private readonly IScoresheetInstanceAppService _scoresheetInstanceAppService; - private readonly IScoresheetAppService _scoresheetAppService; - private readonly ILocalEventBus _localEventBus; + private readonly IIdentityUserIntegrationService _userLookupProvider; private readonly IFeatureChecker _featureChecker; - private readonly IRepository _applicationFormRepository; - private const string UnityFlex = "Unity.Flex"; + private readonly IAssessmentScoresheetService _assessmentScoresheetService; public AssessmentAppService( IAssessmentRepository assessmentRepository, AssessmentManager assessmentManager, IApplicationRepository applicationRepository, IIdentityUserIntegrationService userLookupProvider, - ICommentsManager commentsManager, - IScoresheetInstanceAppService scoresheetInstanceAppService, IFeatureChecker featureChecker, - ILocalEventBus localEventBus, - IScoresheetAppService scoresheetAppService, - IRepository applicationFormRepository) + IAssessmentScoresheetService assessmentScoresheetService) { _assessmentRepository = assessmentRepository; _assessmentManager = assessmentManager; _applicationRepository = applicationRepository; _userLookupProvider = userLookupProvider; - _scoresheetInstanceAppService = scoresheetInstanceAppService; - _scoresheetAppService = scoresheetAppService; _featureChecker = featureChecker; - _localEventBus = localEventBus; - _applicationFormRepository = applicationFormRepository; + _assessmentScoresheetService = assessmentScoresheetService; } public async Task CreateAsync(CreateAssessmentDto dto) @@ -103,7 +82,7 @@ public async Task GetDisplayList(Guid applicationId) bool isApplicationUsingDefaultScoresheet = true; foreach (var assessment in assessmentList) { - var subtotalDto = await GetSubTotal(assessment); + var subtotalDto = await _assessmentScoresheetService.GetSubTotalAsync(assessment); assessment.SubTotal = subtotalDto.SubTotal; if (!subtotalDto.IsUsingDefaultScoresheet) { @@ -113,110 +92,13 @@ public async Task GetDisplayList(Guid applicationId) if (assessmentList.Count == 0) { - isApplicationUsingDefaultScoresheet = await IsScoresheetNotLinkedToForm(applicationId); + var application = await _applicationRepository.GetAsync(applicationId); + isApplicationUsingDefaultScoresheet = await _assessmentScoresheetService.IsScoresheetNotLinkedToFormAsync(application.ApplicationFormId); } return new AssessmentDisplayListDto { Data = assessmentList, IsApplicationUsingDefaultScoresheet = isApplicationUsingDefaultScoresheet }; } - private async Task IsScoresheetNotLinkedToForm(Guid applicationId) - { - var application = await _applicationRepository.GetAsync(applicationId); - var applicationForm = await _applicationFormRepository.GetAsync(application.ApplicationFormId); - if (applicationForm.ScoresheetId == null) - { - return true; - } - else - { - return false; - } - } - - private async Task GetSubTotal(AssessmentListItemDto assessment) - { - if (await _featureChecker.IsEnabledAsync(UnityFlex)) - { - var instance = await _scoresheetInstanceAppService.GetByCorrelationAsync(assessment.Id); - - if (instance == null) - { - - double subTotal = (assessment.FinancialAnalysis ?? 0) + (assessment.EconomicImpact ?? 0) + (assessment.InclusiveGrowth ?? 0) + (assessment.CleanGrowth ?? 0); - return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = true }; - - } - else - { - var questionIds = instance.Answers.Select(a => a.QuestionId).Distinct().ToList(); - - var numericSubtotal = await GetNumericAnswerSubtotal(instance, questionIds); - var yesNoSubtotal = await GetYesNoAnswerSubtotal(instance, questionIds); - var selectListSubtotal = await GetSelectListAnswerSubtotal(instance, questionIds); - - double subTotal = numericSubtotal + yesNoSubtotal + selectListSubtotal; - return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = false }; - - } - } - else - { - double subTotal = (assessment.FinancialAnalysis ?? 0) + (assessment.EconomicImpact ?? 0) + (assessment.InclusiveGrowth ?? 0) + (assessment.CleanGrowth ?? 0); - return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = true }; - } - } - - private async Task GetSelectListAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) - { - var existingSelectListQuestions = await _scoresheetAppService.GetSelectListQuestionsAsync(questionIds); - var existingSelectListQuestionIds = existingSelectListQuestions.Select(a => a.Id).ToList(); - double selectListSubtotal = instance.Answers.Where(a => existingSelectListQuestionIds.Contains(a.QuestionId)) - .Sum(answer => - { - var value = ValueResolver.Resolve(answer.CurrentValue!, QuestionType.SelectList)!.ToString(); - var question = existingSelectListQuestions.Find(q => q.Id == answer.QuestionId) ?? throw new AbpValidationException("Missing QuestionId"); - var definition = JsonSerializer.Deserialize(question.Definition ?? "{}"); - var selectedOption = definition?.Options.Find(o => o.Value == value); - if (selectedOption != null) - { - return selectedOption.NumericValue; - } - else - { - return 0; - } - }); - return selectListSubtotal; - } - - private async Task GetYesNoAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) - { - var existingYesNoQuestions = await _scoresheetAppService.GetYesNoQuestionsAsync(questionIds); - var existingYesNoQuestionIds = existingYesNoQuestions.Select(a => a.Id).ToList(); - double yesNoSubtotal = instance.Answers.Where(a => existingYesNoQuestionIds.Contains(a.QuestionId)) - .Sum(answer => - { - var value = ValueResolver.Resolve(answer.CurrentValue!, QuestionType.YesNo)!.ToString(); - var question = existingYesNoQuestions.Find(q => q.Id == answer.QuestionId) ?? throw new AbpValidationException("Missing QuestionId"); - var definition = JsonSerializer.Deserialize(question.Definition ?? "{}"); - return value switch - { - "Yes" => Convert.ToDouble(definition?.YesValue ?? 0), - "No" => Convert.ToDouble(definition?.NoValue ?? 0), - _ => 0, - }; - }); - return yesNoSubtotal; - } - - private async Task GetNumericAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) - { - var existingNumericQuestionIds = await _scoresheetAppService.GetNumericQuestionIdsAsync(questionIds); - double numericSubtotal = instance.Answers.Where(a => existingNumericQuestionIds.Contains(a.QuestionId)) - .Sum(a => Convert.ToDouble(ValueResolver.Resolve(a.CurrentValue!, QuestionType.Number)!.ToString())); - return numericSubtotal; - } - /// /// If exists, returns the current user's Assessment for an Application. /// @@ -302,31 +184,13 @@ public async Task ExecuteAssessmentAction(Guid assessmentId, Asse await AuthorizationService.CheckAsync(assessment, GetActionAuthorizationRequirement(triggerAction)); - await ApplyAdditionalValidationsAsync(assessmentId, triggerAction); + await _assessmentScoresheetService.ValidateAnswersOnCompleteAsync(assessmentId, triggerAction); await assessment.Workflow.ExecuteActionAsync(triggerAction); return ObjectMapper.Map(await _assessmentRepository.UpdateAsync(assessment, autoSave: true)); } - private async Task ApplyAdditionalValidationsAsync(Guid assessmentId, AssessmentAction triggerAction) - { - await ValidateValidScoresheetAsync(assessmentId, triggerAction); - } - - private async Task ValidateValidScoresheetAsync(Guid assessmentId, AssessmentAction triggerAction) - { - if (await _featureChecker.IsEnabledAsync(UnityFlex) && triggerAction == AssessmentAction.Complete) - { - var requirementsMetResult = await _scoresheetInstanceAppService.ValidateAnswersAsync(assessmentId); - - if (requirementsMetResult?.Errors?.Count > 0) - { - throw new InvalidScoresheetAnswersException([.. requirementsMetResult.Errors]); - } - } - } - private static OperationAuthorizationRequirement GetActionAuthorizationRequirement(AssessmentAction triggerAction) { if (triggerAction == AssessmentAction.SendBack || triggerAction == AssessmentAction.Complete) @@ -385,7 +249,7 @@ public async Task UpdateAssessmentScore(AssessmentScoresDto dto) /// /// Creates a new human assessment by cloning an existing AI assessment. - /// Copies the AI scoresheet answers (from Application.AIScoresheetAnswers) as real + /// Copies the AI scoresheet answers (from AI.ApplicationScoresheetAnswers) as real /// Answer records on the new assessment's scoresheet instance, and carries over /// ApprovalRecommended as a starting point for the reviewer. /// @@ -415,130 +279,11 @@ public async Task CloneFromAiAsync(Guid aiAssessmentId) newAssessment.ApprovalRecommended = aiAssessment.ApprovalRecommended; await _assessmentRepository.UpdateAsync(newAssessment); - if (await _featureChecker.IsEnabledAsync(UnityFlex) && !string.IsNullOrEmpty(application.AIScoresheetAnswers)) - { - await CopyAiAnswersToAssessmentAsync(application.AIScoresheetAnswers, newAssessment.Id); - } + await _assessmentScoresheetService.CopyAiAnswersIfEnabledAsync(aiAssessment.ApplicationId, newAssessment.Id); return ObjectMapper.Map(newAssessment); } - /// - /// Parses Application.AIScoresheetAnswers (JSONB) and writes each AI answer as a - /// real Answer record on the new human assessment's scoresheet instance. - /// - /// Question types are resolved via so that each value - /// is stored in the correct serialized format. SelectList answers are converted from the - /// AI's 1-based numeric index to the actual option value before being persisted. - /// Questions not identified as Numeric, YesNo, or SelectList default to TextArea. - /// - /// - /// Answers are persisted by publishing a - /// local event, reusing the same pipeline as . - /// - /// - private async Task CopyAiAnswersToAssessmentAsync(string aiScoresheetAnswers, Guid newAssessmentId) - { - var rawAiAnswers = new Dictionary(); - try - { - using var doc = JsonDocument.Parse(aiScoresheetAnswers); - foreach (var property in doc.RootElement.EnumerateObject()) - { - if (!Guid.TryParse(property.Name, out var questionId)) continue; - if (property.Value.ValueKind != JsonValueKind.Object) continue; - if (!property.Value.TryGetProperty("answer", out var answerProp)) continue; - rawAiAnswers[questionId] = answerProp.ToString(); - } - } - catch (JsonException) - { - return; - } - - if (rawAiAnswers.Count == 0) return; - - var questionIds = rawAiAnswers.Keys.ToList(); - var numericQuestionIds = (await _scoresheetAppService.GetNumericQuestionIdsAsync(questionIds)).ToHashSet(); - var yesNoQuestions = await _scoresheetAppService.GetYesNoQuestionsAsync(questionIds); - var selectListQuestions = await _scoresheetAppService.GetSelectListQuestionsAsync(questionIds); - var yesNoQuestionIds = yesNoQuestions.Select(q => q.Id).ToHashSet(); - var selectListQuestionIds = selectListQuestions.Select(q => q.Id).ToHashSet(); - - var assessmentAnswers = new List(); - foreach (var (questionId, rawAnswer) in rawAiAnswers) - { - QuestionType questionType; - string answer; - - if (numericQuestionIds.Contains(questionId)) - { - questionType = QuestionType.Number; - answer = rawAnswer; - } - else if (yesNoQuestionIds.Contains(questionId)) - { - questionType = QuestionType.YesNo; - answer = rawAnswer; - } - else if (selectListQuestionIds.Contains(questionId)) - { - questionType = QuestionType.SelectList; - var q = selectListQuestions.Find(x => x.Id == questionId); - answer = ConvertNumericAnswerToSelectListValue(rawAnswer, q?.Definition); - } - else - { - questionType = QuestionType.TextArea; - answer = rawAnswer; - } - - assessmentAnswers.Add(new AssessmentAnswersEto - { - QuestionId = questionId, - Answer = answer, - QuestionType = (int)questionType - }); - } - - if (assessmentAnswers.Count > 0) - { - await _localEventBus.PublishAsync(new PersistScoresheetSectionInstanceEto - { - AssessmentId = newAssessmentId, - AssessmentAnswers = assessmentAnswers - }); - } - } - - /// - /// Converts a 1-based numeric index (as returned by the AI for SelectList questions) - /// to the actual option value defined in the question's JSON definition. - /// Returns the original value unchanged if parsing fails or the index is out of range. - /// - private static string ConvertNumericAnswerToSelectListValue(string numericAnswer, string? definition) - { - if (string.IsNullOrEmpty(definition) || string.IsNullOrEmpty(numericAnswer)) - return numericAnswer; - try - { - if (!int.TryParse(numericAnswer.Trim(), out var optionNumber) || optionNumber <= 0) - return numericAnswer; - var selectListDefinition = JsonSerializer.Deserialize(definition); - if (selectListDefinition?.Options != null && selectListDefinition.Options.Count > 0) - { - var optionIndex = optionNumber - 1; - if (optionIndex < selectListDefinition.Options.Count) - return selectListDefinition.Options[optionIndex].Value; - } - } - catch (JsonException) - { - // Malformed definition — return the raw answer unchanged - } - return numericAnswer; - } - public async Task SaveScoresheetSectionAnswers(AssessmentScoreSectionDto dto) { var assessment = await _assessmentRepository.GetAsync(dto.AssessmentId); @@ -559,14 +304,7 @@ public async Task SaveScoresheetSectionAnswers(AssessmentScoreSectionDto dto) throw new AbpValidationException("Error: This assessment is already completed."); } - if (await _featureChecker.IsEnabledAsync(UnityFlex)) - { - await _localEventBus.PublishAsync(new PersistScoresheetSectionInstanceEto() - { - AssessmentId = dto.AssessmentId, - AssessmentAnswers = dto.AssessmentAnswers - }); - } + await _assessmentScoresheetService.PersistSectionAnswersIfEnabledAsync(dto.AssessmentId, dto.AssessmentAnswers); } else { diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentScoresheetService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentScoresheetService.cs new file mode 100644 index 0000000000..f536c6fcc5 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentScoresheetService.cs @@ -0,0 +1,312 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Unity.Flex; +using Unity.Flex.Scoresheets; +using Unity.Flex.Scoresheets.Enums; +using Unity.Flex.Scoresheets.Events; +using Unity.Flex.Worksheets.Definitions; +using Unity.GrantManager.Applications; +using Unity.GrantManager.Exceptions; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Local; +using Volo.Abp.Features; +using Volo.Abp.Validation; + +namespace Unity.GrantManager.Assessments +{ + /// + /// Coordinates the scoresheet-related concerns of an Assessment: subtotal calculation, + /// checking whether an application's form has a scoresheet linked, validating scoresheet + /// answers on workflow completion, cloning AI-generated answers onto a new human assessment, + /// and persisting scoresheet section answers. Extracted out of + /// so that class's constructor stays focused on assessment-level collaborators. + /// + public class AssessmentScoresheetService : IAssessmentScoresheetService, ITransientDependency + { + private readonly IFeatureChecker _featureChecker; + private readonly IScoresheetInstanceAppService _scoresheetInstanceAppService; + private readonly IScoresheetAppService _scoresheetAppService; + private readonly IRepository _scoresheetAnswersRepository; + private readonly IRepository _applicationFormRepository; + private readonly ILocalEventBus _localEventBus; + private const string UnityFlex = "Unity.Flex"; + + public AssessmentScoresheetService( + IFeatureChecker featureChecker, + IScoresheetInstanceAppService scoresheetInstanceAppService, + IScoresheetAppService scoresheetAppService, + IRepository scoresheetAnswersRepository, + IRepository applicationFormRepository, + ILocalEventBus localEventBus) + { + _featureChecker = featureChecker; + _scoresheetInstanceAppService = scoresheetInstanceAppService; + _scoresheetAppService = scoresheetAppService; + _scoresheetAnswersRepository = scoresheetAnswersRepository; + _applicationFormRepository = applicationFormRepository; + _localEventBus = localEventBus; + } + + public async Task GetSubTotalAsync(AssessmentListItemDto assessment) + { + if (await _featureChecker.IsEnabledAsync(UnityFlex)) + { + var instance = await _scoresheetInstanceAppService.GetByCorrelationAsync(assessment.Id); + + if (instance == null) + { + + double subTotal = (assessment.FinancialAnalysis ?? 0) + (assessment.EconomicImpact ?? 0) + (assessment.InclusiveGrowth ?? 0) + (assessment.CleanGrowth ?? 0); + return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = true }; + + } + else + { + var questionIds = instance.Answers.Select(a => a.QuestionId).Distinct().ToList(); + + var numericSubtotal = await GetNumericAnswerSubtotal(instance, questionIds); + var yesNoSubtotal = await GetYesNoAnswerSubtotal(instance, questionIds); + var selectListSubtotal = await GetSelectListAnswerSubtotal(instance, questionIds); + + double subTotal = numericSubtotal + yesNoSubtotal + selectListSubtotal; + return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = false }; + + } + } + else + { + double subTotal = (assessment.FinancialAnalysis ?? 0) + (assessment.EconomicImpact ?? 0) + (assessment.InclusiveGrowth ?? 0) + (assessment.CleanGrowth ?? 0); + return new SubTotalDto { SubTotal = subTotal, IsUsingDefaultScoresheet = true }; + } + } + + public async Task IsScoresheetNotLinkedToFormAsync(Guid applicationFormId) + { + var applicationForm = await _applicationFormRepository.GetAsync(applicationFormId); + return applicationForm.ScoresheetId == null; + } + + public async Task ValidateAnswersOnCompleteAsync(Guid assessmentId, AssessmentAction triggerAction) + { + if (await _featureChecker.IsEnabledAsync(UnityFlex) && triggerAction == AssessmentAction.Complete) + { + var requirementsMetResult = await _scoresheetInstanceAppService.ValidateAnswersAsync(assessmentId); + + if (requirementsMetResult?.Errors?.Count > 0) + { + throw new InvalidScoresheetAnswersException([.. requirementsMetResult.Errors]); + } + } + } + + /// + /// If the Unity.Flex feature is enabled and stored AI scoresheet answers exist for the + /// given application, copies them onto the given human assessment's scoresheet instance. + /// + public async Task CopyAiAnswersIfEnabledAsync(Guid applicationId, Guid newAssessmentId) + { + if (!await _featureChecker.IsEnabledAsync(UnityFlex)) + { + return; + } + + var storedAiAnswers = await _scoresheetAnswersRepository + .FindAsync(x => x.ApplicationId == applicationId); + + if (string.IsNullOrEmpty(storedAiAnswers?.Answers)) + { + return; + } + + await CopyAiAnswersToAssessmentAsync(storedAiAnswers.Answers, newAssessmentId); + } + + /// + /// If the Unity.Flex feature is enabled, persists the given scoresheet section answers + /// on the given assessment by publishing a + /// local event. + /// + public async Task PersistSectionAnswersIfEnabledAsync(Guid assessmentId, List answers) + { + if (await _featureChecker.IsEnabledAsync(UnityFlex)) + { + await _localEventBus.PublishAsync(new PersistScoresheetSectionInstanceEto() + { + AssessmentId = assessmentId, + AssessmentAnswers = answers + }); + } + } + + private async Task GetSelectListAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) + { + var existingSelectListQuestions = await _scoresheetAppService.GetSelectListQuestionsAsync(questionIds); + var existingSelectListQuestionIds = existingSelectListQuestions.Select(a => a.Id).ToList(); + double selectListSubtotal = instance.Answers.Where(a => existingSelectListQuestionIds.Contains(a.QuestionId)) + .Sum(answer => + { + var value = ValueResolver.Resolve(answer.CurrentValue!, QuestionType.SelectList)!.ToString(); + var question = existingSelectListQuestions.Find(q => q.Id == answer.QuestionId) ?? throw new AbpValidationException("Missing QuestionId"); + var definition = JsonSerializer.Deserialize(question.Definition ?? "{}"); + var selectedOption = definition?.Options.Find(o => o.Value == value); + if (selectedOption != null) + { + return selectedOption.NumericValue; + } + else + { + return 0; + } + }); + return selectListSubtotal; + } + + private async Task GetYesNoAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) + { + var existingYesNoQuestions = await _scoresheetAppService.GetYesNoQuestionsAsync(questionIds); + var existingYesNoQuestionIds = existingYesNoQuestions.Select(a => a.Id).ToList(); + double yesNoSubtotal = instance.Answers.Where(a => existingYesNoQuestionIds.Contains(a.QuestionId)) + .Sum(answer => + { + var value = ValueResolver.Resolve(answer.CurrentValue!, QuestionType.YesNo)!.ToString(); + var question = existingYesNoQuestions.Find(q => q.Id == answer.QuestionId) ?? throw new AbpValidationException("Missing QuestionId"); + var definition = JsonSerializer.Deserialize(question.Definition ?? "{}"); + return value switch + { + "Yes" => Convert.ToDouble(definition?.YesValue ?? 0), + "No" => Convert.ToDouble(definition?.NoValue ?? 0), + _ => 0, + }; + }); + return yesNoSubtotal; + } + + private async Task GetNumericAnswerSubtotal(ScoresheetInstanceDto instance, List questionIds) + { + var existingNumericQuestionIds = await _scoresheetAppService.GetNumericQuestionIdsAsync(questionIds); + double numericSubtotal = instance.Answers.Where(a => existingNumericQuestionIds.Contains(a.QuestionId)) + .Sum(a => Convert.ToDouble(ValueResolver.Resolve(a.CurrentValue!, QuestionType.Number)!.ToString())); + return numericSubtotal; + } + + /// + /// Parses the stored AI scoresheet answers (JSONB) and writes each AI answer as a + /// real Answer record on the new human assessment's scoresheet instance. + /// + /// Question types are resolved via so that each value + /// is stored in the correct serialized format. SelectList answers are converted from the + /// AI's 1-based numeric index to the actual option value before being persisted. + /// Questions not identified as Numeric, YesNo, or SelectList default to TextArea. + /// + /// + /// Answers are persisted by publishing a + /// local event, reusing the same pipeline as . + /// + /// + private async Task CopyAiAnswersToAssessmentAsync(string aiScoresheetAnswers, Guid newAssessmentId) + { + var rawAiAnswers = new Dictionary(); + try + { + using var doc = JsonDocument.Parse(aiScoresheetAnswers); + foreach (var property in doc.RootElement.EnumerateObject()) + { + if (!Guid.TryParse(property.Name, out var questionId)) continue; + if (property.Value.ValueKind != JsonValueKind.Object) continue; + if (!property.Value.TryGetProperty("answer", out var answerProp)) continue; + rawAiAnswers[questionId] = answerProp.ToString(); + } + } + catch (JsonException) + { + return; + } + + if (rawAiAnswers.Count == 0) return; + + var questionIds = rawAiAnswers.Keys.ToList(); + var numericQuestionIds = (await _scoresheetAppService.GetNumericQuestionIdsAsync(questionIds)).ToHashSet(); + var yesNoQuestions = await _scoresheetAppService.GetYesNoQuestionsAsync(questionIds); + var selectListQuestions = await _scoresheetAppService.GetSelectListQuestionsAsync(questionIds); + var yesNoQuestionIds = yesNoQuestions.Select(q => q.Id).ToHashSet(); + var selectListQuestionIds = selectListQuestions.Select(q => q.Id).ToHashSet(); + + var assessmentAnswers = new List(); + foreach (var (questionId, rawAnswer) in rawAiAnswers) + { + QuestionType questionType; + string answer; + + if (numericQuestionIds.Contains(questionId)) + { + questionType = QuestionType.Number; + answer = rawAnswer; + } + else if (yesNoQuestionIds.Contains(questionId)) + { + questionType = QuestionType.YesNo; + answer = rawAnswer; + } + else if (selectListQuestionIds.Contains(questionId)) + { + questionType = QuestionType.SelectList; + var q = selectListQuestions.Find(x => x.Id == questionId); + answer = ConvertNumericAnswerToSelectListValue(rawAnswer, q?.Definition); + } + else + { + questionType = QuestionType.TextArea; + answer = rawAnswer; + } + + assessmentAnswers.Add(new AssessmentAnswersEto + { + QuestionId = questionId, + Answer = answer, + QuestionType = (int)questionType + }); + } + + if (assessmentAnswers.Count > 0) + { + await _localEventBus.PublishAsync(new PersistScoresheetSectionInstanceEto + { + AssessmentId = newAssessmentId, + AssessmentAnswers = assessmentAnswers + }); + } + } + + /// + /// Converts a 1-based numeric index (as returned by the AI for SelectList questions) + /// to the actual option value defined in the question's JSON definition. + /// Returns the original value unchanged if parsing fails or the index is out of range. + /// + private static string ConvertNumericAnswerToSelectListValue(string numericAnswer, string? definition) + { + if (string.IsNullOrEmpty(definition) || string.IsNullOrEmpty(numericAnswer)) + return numericAnswer; + try + { + if (!int.TryParse(numericAnswer.Trim(), out var optionNumber) || optionNumber <= 0) + return numericAnswer; + var selectListDefinition = JsonSerializer.Deserialize(definition); + if (selectListDefinition?.Options != null && selectListDefinition.Options.Count > 0) + { + var optionIndex = optionNumber - 1; + if (optionIndex < selectListDefinition.Options.Count) + return selectListDefinition.Options[optionIndex].Value; + } + } + catch (JsonException) + { + // Malformed definition — return the raw answer unchanged + } + return numericAnswer; + } + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/IAssessmentScoresheetService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/IAssessmentScoresheetService.cs new file mode 100644 index 0000000000..2991c245af --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/IAssessmentScoresheetService.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Unity.Flex.Scoresheets.Events; + +namespace Unity.GrantManager.Assessments +{ + /// + /// Scoresheet-related concerns of an Assessment, extracted out of . + /// Internal collaborator, not exposed as a public API. + /// + public interface IAssessmentScoresheetService + { + Task GetSubTotalAsync(AssessmentListItemDto assessment); + + Task IsScoresheetNotLinkedToFormAsync(Guid applicationFormId); + + Task ValidateAnswersOnCompleteAsync(Guid assessmentId, AssessmentAction triggerAction); + + Task CopyAiAnswersIfEnabledAsync(Guid applicationId, Guid newAssessmentId); + + Task PersistSectionAnswersIfEnabledAsync(Guid assessmentId, List answers); + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Generation/BackgroundJobs/AIGenerationRequestJobHelper.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Generation/BackgroundJobs/AIGenerationRequestJobHelper.cs index e51dea4f78..13b8a39ee3 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Generation/BackgroundJobs/AIGenerationRequestJobHelper.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Generation/BackgroundJobs/AIGenerationRequestJobHelper.cs @@ -7,6 +7,7 @@ using Unity.AI.RateLimit; using Unity.GrantManager.Applications; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; using Volo.Abp.Uow; namespace Unity.GrantManager.GrantApplications.Automation.BackgroundJobs; @@ -33,6 +34,35 @@ public static async Task SaveApplicationResultInNewUowAsync( await uow.CompleteAsync(); } + /// + /// Upserts the AI scoresheet answers for an application in a fresh, short-lived unit of + /// work. Answers live in their own aggregate (AI.ApplicationScoresheetAnswers) rather than + /// on the Application, so regenerating scoring updates the existing row in place. + /// + public static async Task SaveScoresheetAnswersInNewUowAsync( + IUnitOfWorkManager unitOfWorkManager, + IRepository scoresheetAnswersRepository, + IGuidGenerator guidGenerator, + Guid applicationId, + string answers) + { + using var uow = unitOfWorkManager.Begin(requiresNew: true, isTransactional: false); + + var existing = await scoresheetAnswersRepository.FindAsync(x => x.ApplicationId == applicationId); + if (existing == null) + { + await scoresheetAnswersRepository.InsertAsync( + new ApplicationScoresheetAnswers(guidGenerator.Create(), applicationId, answers)); + } + else + { + existing.SetAnswers(answers); + await scoresheetAnswersRepository.UpdateAsync(existing); + } + + await uow.CompleteAsync(); + } + public static async Task MarkRunningAsync( IRepository generationRequestRepository, AIGenerationRequest? request) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Operations/ApplicationScoring/ApplicationScoringOperationExecutor.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Operations/ApplicationScoring/ApplicationScoringOperationExecutor.cs index 3af1ad99ae..e0405ef970 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Operations/ApplicationScoring/ApplicationScoringOperationExecutor.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantApplications/Automation/Operations/ApplicationScoring/ApplicationScoringOperationExecutor.cs @@ -10,6 +10,7 @@ using Volo.Abp.BackgroundJobs; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; using Volo.Abp.EventBus.Local; using Volo.Abp.MultiTenancy; using Volo.Abp.Uow; @@ -22,6 +23,8 @@ public sealed class ApplicationScoringOperationExecutor( ApplicationScoringService applicationScoringService, IAIApplicationInputBuilder aiApplicationInputBuilder, IApplicationRepository applicationRepository, + IRepository scoresheetAnswersRepository, + IGuidGenerator guidGenerator, IUnitOfWorkManager unitOfWorkManager, ILocalEventBus localEventBus, IObjectMapper objectMapper) : AIGenerationOperationExecutor, ITransientDependency @@ -35,11 +38,12 @@ protected override async Task ExecuteAsync(AIGenerationBackgroundJobArgs a var scoringInput = await aiApplicationInputBuilder.BuildApplicationScoringInputAsync(promptData, args.PromptVersion); var scoresheetAnswers = await applicationScoringService.RegenerateAsync(scoringInput); - await AIGenerationRequestJobHelper.SaveApplicationResultInNewUowAsync( + await AIGenerationRequestJobHelper.SaveScoresheetAnswersInNewUowAsync( unitOfWorkManager, - applicationRepository, + scoresheetAnswersRepository, + guidGenerator, args.ApplicationId, - app => app.AIScoresheetAnswers = scoresheetAnswers); + scoresheetAnswers); await localEventBus.PublishAsync(new ApplicationAIScoringGeneratedEvent { diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/Application.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/Application.cs index 16d7a932c5..2a1f6a327b 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/Application.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/Application.cs @@ -146,9 +146,6 @@ public virtual ApplicationStatus ApplicationStatus public bool EligibleForRenewal { get; set; } - [Column(TypeName = "jsonb")] - public string? AIScoresheetAnswers { get; set; } - public bool IsInFinalDecisionState() { return GrantApplicationStateGroups.FinalDecisionStates.Contains(ApplicationStatus.StatusCode); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationScoresheetAnswers.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationScoresheetAnswers.cs new file mode 100644 index 0000000000..793f35c01e --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationScoresheetAnswers.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.MultiTenancy; + +namespace Unity.GrantManager.Applications; + +/// +/// AI-generated scoresheet answers for a single application. One row per application, +/// holding the whole answer set as JSON keyed by scoresheet question id. +/// +/// Previously stored as the Applications.AIScoresheetAnswers jsonb column. Moved to its own +/// table in the AI schema so the Applications table stays legible to report builders, and so +/// AI output is not written back onto the Application aggregate. +/// +/// +public class ApplicationScoresheetAnswers : AuditedAggregateRoot, IMultiTenant +{ + protected ApplicationScoresheetAnswers() + { + Answers = "{}"; + } + + public ApplicationScoresheetAnswers( + Guid id, + Guid applicationId, + string answers) + : base(id) + { + ApplicationId = applicationId; + Answers = answers; + } + + public Guid ApplicationId { get; private set; } + + [Column(TypeName = "jsonb")] + public string Answers { get; private set; } + + public Guid? TenantId { get; set; } + + public void SetAnswers(string answers) + { + Answers = answers; + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/EntityFrameworkCore/GrantTenantDbContext.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/EntityFrameworkCore/GrantTenantDbContext.cs index a25e36b48c..a2d64b3069 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/EntityFrameworkCore/GrantTenantDbContext.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/EntityFrameworkCore/GrantTenantDbContext.cs @@ -35,6 +35,7 @@ public class GrantTenantDbContext : AbpDbContext public DbSet GenerationReviews { get; set; } public DbSet Applicants { get; set; } public DbSet Applications { get; set; } + public DbSet ApplicationScoresheetAnswers { get; set; } public DbSet ApplicationStatuses { get; set; } public DbSet ApplicationUserAssignments { get; set; } public DbSet ApplicationChefsFileAttachments { get; set; } @@ -178,6 +179,14 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) b.HasIndex(x => new { x.Operation, x.ContextId, x.Sequence }).IsUnique(); }); + modelBuilder.Entity(b => + { + b.ToTable(GrantManagerConsts.TenantTablePrefix + "ApplicationScoresheetAnswers", "AI"); + b.ConfigureByConvention(); + b.Property(x => x.Answers).HasColumnType("jsonb").IsRequired(); + b.HasIndex(x => x.ApplicationId).IsUnique(); + }); + modelBuilder.Entity(b => { b.ToTable(GrantManagerConsts.TenantTablePrefix + "ApplicationStatuses", diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.Designer.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.Designer.cs new file mode 100644 index 0000000000..696be5aede --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.Designer.cs @@ -0,0 +1,5577 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Unity.GrantManager.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace Unity.GrantManager.Migrations.TenantMigrations +{ + [DbContext(typeof(GrantTenantDbContext))] + [Migration("20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema")] + partial class AB33327_MoveAIScoresheetAnswersToAISchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Unity.Flex.Domain.ScoresheetInstances.ScoresheetInstance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("CorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ScoresheetId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ScoresheetId"); + + b.ToTable("ScoresheetInstances", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Answer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("CurrentValue") + .HasColumnType("jsonb"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("QuestionId") + .HasColumnType("uuid"); + + b.Property("ScoresheetInstanceId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("QuestionId"); + + b.HasIndex("ScoresheetInstanceId"); + + b.ToTable("Answers", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Definition") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("SectionId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("Questions", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Scoresheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsArchived") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("Published") + .HasColumnType("boolean"); + + b.Property("ReportColumns") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportKeys") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportViewName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Scoresheets", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.ScoresheetSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("ScoresheetId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ScoresheetId"); + + b.ToTable("ScoresheetSections", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetInstances.CustomFieldValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("CurrentValue") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("CustomFieldId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("WorksheetInstanceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorksheetInstanceId"); + + b.ToTable("CustomFieldValues", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetInstances.WorksheetInstance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("CorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("CurrentValue") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UiAnchor") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorksheetCorrelationId") + .HasColumnType("uuid"); + + b.Property("WorksheetCorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorksheetId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("WorksheetInstances", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetLinks.WorksheetLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("CorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UiAnchor") + .IsRequired() + .HasColumnType("text"); + + b.Property("WorksheetId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorksheetId"); + + b.ToTable("WorksheetLinks", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.CustomField", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Definition") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("SectionId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SectionId"); + + b.ToTable("CustomFields", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.Worksheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsArchived") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Published") + .HasColumnType("boolean"); + + b.Property("ReportColumns") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportKeys") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportViewName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("Worksheets", "Flex"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.WorksheetSection", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Definition") + .HasColumnType("jsonb"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Order") + .HasColumnType("bigint"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("WorksheetId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("WorksheetId"); + + b.ToTable("WorksheetSections", "Flex"); + }); + + modelBuilder.Entity("Unity.GrantManager.ApplicationForms.GenerationReview", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContextId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Operation") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReviewData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Sequence") + .HasColumnType("integer"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Operation", "ContextId", "Sequence") + .IsUnique(); + + b.ToTable("GenerationReviews", "AI"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.Applicant", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantName") + .IsRequired() + .HasMaxLength(600) + .HasColumnType("character varying(600)"); + + b.Property("ApproxNumberOfEmployees") + .HasColumnType("text"); + + b.Property("AuditComments") + .HasColumnType("text"); + + b.Property("BusinessNumber") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FiscalDay") + .HasColumnType("integer"); + + b.Property("FiscalMonth") + .HasColumnType("text"); + + b.Property("FiscalYearEnd") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("date"); + + b.Property("FundingHistoryComments") + .HasColumnType("text"); + + b.Property("IndigenousOrgInd") + .HasColumnType("text"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsDuplicated") + .HasColumnType("boolean"); + + b.Property("IssueTrackingComments") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MatchPercentage") + .HasColumnType("numeric"); + + b.Property("NonRegOrgName") + .HasColumnType("text"); + + b.Property("NonRegisteredBusinessName") + .HasColumnType("text"); + + b.Property("OrgName") + .HasColumnType("text"); + + b.Property("OrgNumber") + .HasColumnType("text"); + + b.Property("OrgStatus") + .HasColumnType("text"); + + b.Property("OrganizationType") + .HasColumnType("text"); + + b.Property("RedStop") + .HasColumnType("boolean"); + + b.Property("ReportsComments") + .HasColumnType("text"); + + b.Property("Sector") + .HasColumnType("text"); + + b.Property("SectorSubSectorIndustryDesc") + .HasColumnType("text"); + + b.Property("StartedOperatingDate") + .HasColumnType("date"); + + b.Property("Status") + .HasColumnType("text"); + + b.Property("SubSector") + .HasColumnType("text"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UnityApplicantId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantName"); + + b.HasIndex("OrgName"); + + b.HasIndex("OrgNumber"); + + b.HasIndex("Status"); + + b.HasIndex("SupplierId"); + + b.HasIndex("TenantId"); + + b.HasIndex("UnityApplicantId"); + + b.HasIndex("TenantId", "IsDeleted", "CreationTime") + .HasFilter("\"IsDeleted\" = false"); + + b.ToTable("Applicants", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAddress", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AddressType") + .HasColumnType("integer"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Postal") + .HasColumnType("text"); + + b.Property("Province") + .HasColumnType("text"); + + b.Property("Street") + .HasColumnType("text"); + + b.Property("Street2") + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Unit") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.HasIndex("ApplicationId"); + + b.ToTable("ApplicantAddresses", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAgent", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("BceidBusinessGuid") + .HasColumnType("uuid"); + + b.Property("BceidBusinessName") + .HasColumnType("text"); + + b.Property("BceidUserGuid") + .HasColumnType("uuid"); + + b.Property("BceidUserName") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContactOrder") + .HasColumnType("integer"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IdentityEmail") + .HasColumnType("text"); + + b.Property("IdentityName") + .HasColumnType("text"); + + b.Property("IdentityProvider") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsConfirmed") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OidcSubUser") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("Phone2") + .HasColumnType("text"); + + b.Property("Phone2Extension") + .HasColumnType("text"); + + b.Property("PhoneExtension") + .HasColumnType("text"); + + b.Property("RoleForApplicant") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Title") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.HasIndex("ApplicationId") + .IsUnique(); + + b.HasIndex("TenantId", "ApplicationId"); + + b.ToTable("ApplicantAgents", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAttachment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("S3ObjectKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Time") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.ToTable("ApplicantAttachments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.Application", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AIAnalysis") + .HasColumnType("text"); + + b.Property("Acquisition") + .HasColumnType("text"); + + b.Property("ApplicantElectoralDistrict") + .HasColumnType("text"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApplicationFormId") + .HasColumnType("uuid"); + + b.Property("ApplicationStatusId") + .HasColumnType("uuid"); + + b.Property("ApprovedAmount") + .HasColumnType("numeric"); + + b.Property("AssessmentResultDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AssessmentResultStatus") + .HasColumnType("text"); + + b.Property("AssessmentStartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Community") + .HasColumnType("text"); + + b.Property("CommunityPopulation") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContractExecutionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ContractNumber") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeclineRational") + .HasColumnType("text"); + + b.Property("DefaultSiteId") + .HasColumnType("uuid"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("DueDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DueDiligenceStatus") + .HasColumnType("text"); + + b.Property("EconomicRegion") + .HasColumnType("text"); + + b.Property("ElectoralDistrict") + .HasColumnType("text"); + + b.Property("EligibleForRenewal") + .HasColumnType("boolean"); + + b.Property("ExternalStatusVisibility") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FinalDecisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("Forestry") + .HasColumnType("text"); + + b.Property("ForestryFocus") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LikelihoodOfFunding") + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("NotificationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property("Payload") + .HasColumnType("jsonb"); + + b.Property("PercentageTotalProjectBudget") + .HasColumnType("double precision"); + + b.Property("Place") + .HasColumnType("text"); + + b.Property("ProjectEndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ProjectFundingTotal") + .HasColumnType("numeric"); + + b.Property("ProjectName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ProjectStartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ProjectSummary") + .HasColumnType("text"); + + b.Property("ProposalDate") + .HasColumnType("timestamp without time zone"); + + b.Property("RecommendedAmount") + .HasColumnType("numeric"); + + b.Property("ReferenceNo") + .IsRequired() + .HasColumnType("text"); + + b.Property("RegionalDistrict") + .HasColumnType("text"); + + b.Property("RequestedAmount") + .HasColumnType("numeric"); + + b.Property("RiskRanking") + .HasColumnType("text"); + + b.Property("SigningAuthorityBusinessPhone") + .HasColumnType("text"); + + b.Property("SigningAuthorityCellPhone") + .HasColumnType("text"); + + b.Property("SigningAuthorityEmail") + .HasColumnType("text"); + + b.Property("SigningAuthorityFullName") + .HasColumnType("text"); + + b.Property("SigningAuthorityTitle") + .HasColumnType("text"); + + b.Property("SubStatus") + .HasColumnType("text"); + + b.Property("SubmissionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalProjectBudget") + .HasColumnType("numeric"); + + b.Property("TotalScore") + .HasColumnType("integer"); + + b.Property("UnityApplicationId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.HasIndex("ApplicationFormId"); + + b.HasIndex("ApplicationStatusId"); + + b.HasIndex("OwnerId"); + + b.HasIndex("ReferenceNo"); + + b.HasIndex("TenantId", "SubmissionDate") + .HasFilter("\"IsDeleted\" = false"); + + b.ToTable("Applications", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationAssignment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("AssigneeId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Duty") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("AssigneeId"); + + b.HasIndex("TenantId", "ApplicationId"); + + b.ToTable("ApplicationAssignments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationAttachment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("S3ObjectKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Time") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.ToTable("ApplicationAttachments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationChefsFileAttachment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AISummary") + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ChefsFileId") + .HasColumnType("text"); + + b.Property("ChefsSubmissionId") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.ToTable("ApplicationChefsFileAttachments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationContact", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContactEmail") + .HasColumnType("text"); + + b.Property("ContactFullName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContactMobilePhone") + .HasColumnType("text"); + + b.Property("ContactTitle") + .HasColumnType("text"); + + b.Property("ContactType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContactWorkPhone") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.ToTable("ApplicationContact", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationForm", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccountCodingId") + .HasColumnType("uuid"); + + b.Property("ApiKey") + .HasColumnType("text"); + + b.Property("ApplicationFormDescription") + .HasColumnType("text"); + + b.Property("ApplicationFormName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AttemptedConnectionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AutomaticallyGenerateAIAnalysis") + .HasColumnType("boolean"); + + b.Property("AvailableChefsFields") + .HasColumnType("text"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("ChefsApplicationFormGuid") + .HasColumnType("text"); + + b.Property("ChefsCriteriaFormGuid") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConnectionHttpStatus") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DefaultPaymentGroup") + .HasColumnType("integer"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ElectoralDistrictAddressType") + .HasColumnType("integer"); + + b.Property("ExternalLinksConfig") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("ExternalLinks"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FormHierarchy") + .HasColumnType("integer"); + + b.Property("IntakeId") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDirectApproval") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ManuallyInitiateAIAnalysis") + .HasColumnType("boolean"); + + b.Property("ParentFormId") + .HasColumnType("uuid"); + + b.Property("Payable") + .HasColumnType("boolean"); + + b.Property("PaymentApprovalThreshold") + .HasColumnType("numeric"); + + b.Property("Prefix") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PreventPayment") + .HasColumnType("boolean"); + + b.Property("ScoresheetId") + .HasColumnType("uuid"); + + b.Property("SuffixType") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IntakeId"); + + b.HasIndex("ParentFormId"); + + b.HasIndex("TenantId", "IsDeleted") + .HasFilter("\"IsDeleted\" = false"); + + b.ToTable("ApplicationForms", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationFormSubmission", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApplicationFormId") + .HasColumnType("uuid"); + + b.Property("ApplicationFormVersionId") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ChefsSubmissionGuid") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FormVersionId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OidcSub") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportData") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Submission") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.HasIndex("ApplicationFormId"); + + b.ToTable("ApplicationFormSubmissions", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationFormVersion", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationFormId") + .HasColumnType("uuid"); + + b.Property("AvailableChefsFields") + .HasColumnType("text"); + + b.Property("ChefsApplicationFormGuid") + .HasColumnType("text"); + + b.Property("ChefsFormVersionGuid") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FormSchema") + .HasColumnType("jsonb"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Published") + .HasColumnType("boolean"); + + b.Property("ReportColumns") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportKeys") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReportViewName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SubmissionHeaderMapping") + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationFormId"); + + b.ToTable("ApplicationFormVersion", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationLink", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LinkType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("Related"); + + b.Property("LinkedApplicationId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("TenantId", "ApplicationId"); + + b.ToTable("ApplicationLinks", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationScoresheetAnswers", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Answers") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId") + .IsUnique(); + + b.ToTable("ApplicationScoresheetAnswers", "AI"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationStatus", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExternalStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("InternalStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("NotifiedStatus") + .HasColumnType("text"); + + b.Property("StatusCode") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("StatusCode") + .IsUnique(); + + b.ToTable("ApplicationStatuses", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationTags", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TagId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("TagId"); + + b.HasIndex("TenantId", "ApplicationId"); + + b.ToTable("ApplicationTags", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.AssessmentAttachment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AssessmentId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("S3ObjectKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Time") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AssessmentId"); + + b.ToTable("AssessmentAttachments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.AuditHistory", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("AuditDate") + .HasColumnType("timestamp without time zone"); + + b.Property("AuditNote") + .HasColumnType("text"); + + b.Property("AuditStatus") + .HasColumnType("text"); + + b.Property("AuditTrackingNumber") + .HasColumnType("text"); + + b.Property("AuditorName") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.ToTable("AuditHistories", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.FundingHistory", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApprovedAmount") + .HasColumnType("numeric"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FundingNotes") + .HasColumnType("text"); + + b.Property("FundingYear") + .HasColumnType("text"); + + b.Property("GrantCategory") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OneTimeConsideration") + .HasColumnType("numeric"); + + b.Property("PaidDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ReconsiderationAmount") + .HasColumnType("numeric"); + + b.Property("RenewedFunding") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TotalGrantAmount") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.ToTable("FundingHistories", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.IssueTracking", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IssueDescription") + .HasColumnType("text"); + + b.Property("IssueHeading") + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ResolutionNote") + .HasColumnType("text"); + + b.Property("Resolved") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Year") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.ToTable("IssueTrackings", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ReportsHistory", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FiscalYear") + .HasColumnType("text"); + + b.Property("IncompleteReport") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("Outstanding") + .HasColumnType("boolean"); + + b.Property("ReportDate") + .HasColumnType("timestamp without time zone"); + + b.Property("SignedOff") + .HasColumnType("boolean"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.ToTable("ReportsHistories", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Assessments.Assessment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ApprovalRecommended") + .HasColumnType("boolean"); + + b.Property("AssessorId") + .HasColumnType("uuid"); + + b.Property("CleanGrowth") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EconomicImpact") + .HasColumnType("integer"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FinancialAnalysis") + .HasColumnType("integer"); + + b.Property("InclusiveGrowth") + .HasColumnType("integer"); + + b.Property("IsAiAssessment") + .HasColumnType("boolean"); + + b.Property("IsComplete") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("AssessorId"); + + b.ToTable("Assessments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.ApplicantComment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text"); + + b.Property("CommenterId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PinDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantId"); + + b.HasIndex("CommenterId"); + + b.ToTable("ApplicantComments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.ApplicationComment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text"); + + b.Property("CommenterId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PinDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("CommenterId"); + + b.ToTable("ApplicationComments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.AssessmentComment", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AssessmentId") + .HasColumnType("uuid"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text"); + + b.Property("CommenterId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PinDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AssessmentId"); + + b.HasIndex("CommenterId"); + + b.ToTable("AssessmentComments", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Contacts.Contact", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Email") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HomePhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MobilePhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Title") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("WorkPhoneExtension") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("WorkPhoneNumber") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.ToTable("Contacts", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Contacts.ContactLink", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContactId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsPrimary") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("RelatedEntityId") + .HasColumnType("uuid"); + + b.Property("RelatedEntityType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Role") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RelatedEntityType", "RelatedEntityId"); + + b.HasIndex("ContactId", "RelatedEntityType", "RelatedEntityId"); + + b.ToTable("ContactLinks", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.GlobalTag.Tag", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("Tags", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Identity.Person", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Badge") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FullName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OidcDisplayName") + .IsRequired() + .HasColumnType("text"); + + b.Property("OidcSub") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("OidcSub"); + + b.HasIndex("TenantId"); + + b.ToTable("Persons", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Intakes.Intake", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Budget") + .HasColumnType("double precision"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("EndDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IntakeName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("StartDate") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("Intakes", (string)null); + }); + + modelBuilder.Entity("Unity.GrantManager.Notifications.ScheduledNotification", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationStatus") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ApplicationStatusId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DateField") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("EmailTemplateId") + .HasColumnType("uuid"); + + b.Property("EventType") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FormId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Module") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("RecipientCategory") + .HasColumnType("text"); + + b.Property("RecipientIdentifier") + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TriggerDetail") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("TriggerType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId"); + + b.ToTable("ScheduledNotifications", "Notifications"); + }); + + modelBuilder.Entity("Unity.GrantManager.Notifications.ScheduledNotificationTracking", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DateField") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NotificationSentDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ScheduledNotificationId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId"); + + b.HasIndex("CreationTime"); + + b.HasIndex("ScheduledNotificationId"); + + b.HasIndex("ApplicationId", "ScheduledNotificationId", "DateField") + .IsUnique(); + + b.ToTable("ScheduledNotificationTracking", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.EmailGroups.EmailGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EmailGroups", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.EmailGroups.EmailGroupUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.ToTable("EmailGroupUsers", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Emails.EmailLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApplicantId") + .HasColumnType("uuid"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("AssessmentId") + .HasColumnType("uuid"); + + b.Property("BCC") + .IsRequired() + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasColumnType("text"); + + b.Property("BodyType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CC") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChesHttpStatusCode") + .HasColumnType("text"); + + b.Property("ChesMsgId") + .HasColumnType("uuid"); + + b.Property("ChesResponse") + .IsRequired() + .HasColumnType("text"); + + b.Property("ChesStatus") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EmailType") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FromAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PaymentRequestIds") + .IsRequired() + .HasColumnType("text"); + + b.Property("Priority") + .IsRequired() + .HasColumnType("text"); + + b.Property("Recipient") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("RetryAttempts") + .HasColumnType("integer"); + + b.Property("ScheduledNotificationId") + .HasColumnType("uuid"); + + b.Property("SendOnDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("SentDateTime") + .HasColumnType("timestamp without time zone"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tag") + .IsRequired() + .HasColumnType("text"); + + b.Property("TemplateName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("ToAddress") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("EmailLogs", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Emails.EmailLogAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContentType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DisplayName") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("EmailLogId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OriginTemplateId") + .HasColumnType("uuid"); + + b.Property("S3ObjectKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("TemplateId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Time") + .HasColumnType("timestamp without time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("EmailLogId"); + + b.HasIndex("S3ObjectKey"); + + b.HasIndex("TemplateId"); + + b.ToTable("EmailLogAttachments", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Logs.NotificationLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Channel") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("CommitSha") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeliveryTarget") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Environment") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ExceptionMessage") + .HasColumnType("text"); + + b.Property("ExceptionType") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeliveredRealtime") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("NotificationType") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PayloadJson") + .HasColumnType("jsonb"); + + b.Property("SenderDisplayName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SenderUserId") + .HasColumnType("uuid"); + + b.Property("Severity") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("Source") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("SourceReference") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("StackExcerpt") + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("NotificationType", "CreationTime"); + + b.HasIndex("TenantId", "CreationTime"); + + b.ToTable("NotificationLogs", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.ReadStates.NotificationReadState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastReadAt") + .HasColumnType("timestamp without time zone"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "UserId") + .IsUnique(); + + b.ToTable("NotificationReadStates", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.EmailTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BodyHTML") + .IsRequired() + .HasColumnType("text"); + + b.Property("BodyText") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("RecipientCategory") + .HasColumnType("text"); + + b.Property("RecipientIdentifier") + .HasColumnType("text"); + + b.Property("SendFrom") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("EmailTemplates", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.Subscriber", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("Subscribers", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.SubscriptionGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("SubscriptionGroups", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.SubscriptionGroupSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("GroupId") + .HasColumnType("uuid"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("SubscriberId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("GroupId"); + + b.HasIndex("SubscriberId"); + + b.ToTable("SubscriptionGroupSubscribers", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.TemplateVariable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MapTo") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("TemplateVariables", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.Trigger", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("InternalName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("Triggers", "Notifications"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.TriggerSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("SubscriptionGroupId") + .HasColumnType("uuid"); + + b.Property("TemplateId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TriggerId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SubscriptionGroupId"); + + b.HasIndex("TemplateId"); + + b.HasIndex("TriggerId"); + + b.ToTable("TriggerSubscriptions", "Notifications"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.AccountCodings.AccountCoding", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Description") + .HasMaxLength(35) + .HasColumnType("character varying(35)"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MinistryClient") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("Responsibility") + .IsRequired() + .HasColumnType("text"); + + b.Property("ServiceLine") + .IsRequired() + .HasColumnType("text"); + + b.Property("Stob") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("AccountCodings", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentConfigurations.PaymentConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DefaultAccountCodingId") + .HasColumnType("uuid"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PaymentIdPrefix") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("PaymentConfigurations", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentRequests.ExpenseApproval", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DecisionDate") + .HasColumnType("timestamp without time zone"); + + b.Property("DecisionUserId") + .HasColumnType("uuid"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PaymentRequestId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("PaymentRequestId"); + + b.ToTable("ExpenseApprovals", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentRequests.PaymentRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountCodingId") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("numeric"); + + b.Property("BatchName") + .IsRequired() + .HasColumnType("text"); + + b.Property("BatchNumber") + .HasColumnType("numeric"); + + b.Property("CancelledBy") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("CancelledBy"); + + b.Property("CancelledById") + .HasColumnType("uuid") + .HasColumnName("CancelledById"); + + b.Property("CancelledOn") + .HasColumnType("timestamp without time zone") + .HasColumnName("CancelledOn"); + + b.Property("CasHttpStatusCode") + .HasColumnType("integer"); + + b.Property("CasResponse") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContractNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("CorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FsbApNotified") + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("FsbNotificationEmailLogId") + .HasColumnType("uuid"); + + b.Property("FsbNotificationSentDate") + .HasColumnType("timestamp without time zone"); + + b.Property("InvoiceNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("InvoiceStatus") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsRecon") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("PayeeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("PaymentDate") + .HasColumnType("text"); + + b.Property("PaymentNumber") + .HasColumnType("text"); + + b.Property("PaymentStatus") + .HasColumnType("text"); + + b.Property("ReferenceNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("RequesterName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SiteId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("SubmissionConfirmationCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("SupplierName") + .HasColumnType("text"); + + b.Property("SupplierNumber") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AccountCodingId"); + + b.HasIndex("CorrelationId"); + + b.HasIndex("CreationTime"); + + b.HasIndex("FsbNotificationEmailLogId"); + + b.HasIndex("ReferenceNumber") + .IsUnique(); + + b.HasIndex("SiteId"); + + b.HasIndex("Status"); + + b.HasIndex("TenantId", "CreationTime") + .HasFilter("\"IsDeleted\" = false"); + + b.ToTable("PaymentRequests", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentTags.PaymentTag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("PaymentRequestId") + .HasColumnType("uuid"); + + b.Property("TagId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("PaymentRequestId"); + + b.HasIndex("TagId"); + + b.ToTable("PaymentTags", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentThresholds.PaymentThreshold", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Threshold") + .HasColumnType("numeric"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("PaymentThresholds", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.Suppliers.Site", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddressLine1") + .HasColumnType("text"); + + b.Property("AddressLine2") + .HasColumnType("text"); + + b.Property("AddressLine3") + .HasColumnType("text"); + + b.Property("BankAccount") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("EFTAdvicePref") + .HasColumnType("text"); + + b.Property("EmailAddress") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastUpdatedInCas") + .HasColumnType("timestamp without time zone"); + + b.Property("MarkDeletedInUse") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasColumnType("text"); + + b.Property("PaymentGroup") + .HasColumnType("integer"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("text"); + + b.Property("Province") + .HasColumnType("text"); + + b.Property("SiteProtected") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("text"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("SupplierId"); + + b.ToTable("Sites", "Payments"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.Suppliers.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BusinessNumber") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("DeletionTime"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LastUpdatedInCAS") + .HasColumnType("timestamp without time zone"); + + b.Property("MailingAddress") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("text"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("ProviderId") + .HasColumnType("text"); + + b.Property("Province") + .HasColumnType("text"); + + b.Property("SIN") + .HasColumnType("text"); + + b.Property("StandardIndustryClassification") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("text"); + + b.Property("Subcategory") + .HasColumnType("text"); + + b.Property("SupplierProtected") + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.ToTable("Suppliers", "Payments"); + }); + + modelBuilder.Entity("Unity.Reporting.Domain.Configuration.ReportColumnsMap", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("CorrelationProvider") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Mapping") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("RoleStatus") + .HasColumnType("integer"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("ViewName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ViewStatus") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ReportColumnsMaps", "Reporting"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.ScoresheetInstances.ScoresheetInstance", b => + { + b.HasOne("Unity.Flex.Domain.Scoresheets.Scoresheet", "Scoresheet") + .WithMany("Instances") + .HasForeignKey("ScoresheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scoresheet"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Answer", b => + { + b.HasOne("Unity.Flex.Domain.Scoresheets.Question", "Question") + .WithMany("Answers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.Flex.Domain.ScoresheetInstances.ScoresheetInstance", null) + .WithMany("Answers") + .HasForeignKey("ScoresheetInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Question", b => + { + b.HasOne("Unity.Flex.Domain.Scoresheets.ScoresheetSection", "Section") + .WithMany("Fields") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.ScoresheetSection", b => + { + b.HasOne("Unity.Flex.Domain.Scoresheets.Scoresheet", "Scoresheet") + .WithMany("Sections") + .HasForeignKey("ScoresheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Scoresheet"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetInstances.CustomFieldValue", b => + { + b.HasOne("Unity.Flex.Domain.WorksheetInstances.WorksheetInstance", null) + .WithMany("Values") + .HasForeignKey("WorksheetInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetLinks.WorksheetLink", b => + { + b.HasOne("Unity.Flex.Domain.Worksheets.Worksheet", "Worksheet") + .WithMany("Links") + .HasForeignKey("WorksheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Worksheet"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.CustomField", b => + { + b.HasOne("Unity.Flex.Domain.Worksheets.WorksheetSection", "Section") + .WithMany("Fields") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.WorksheetSection", b => + { + b.HasOne("Unity.Flex.Domain.Worksheets.Worksheet", "Worksheet") + .WithMany("Sections") + .HasForeignKey("WorksheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Worksheet"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAddress", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", "Applicant") + .WithMany("ApplicantAddresses") + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.Application", "Application") + .WithMany("ApplicantAddresses") + .HasForeignKey("ApplicationId"); + + b.Navigation("Applicant"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAgent", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.Application", "Application") + .WithOne("ApplicantAgent") + .HasForeignKey("Unity.GrantManager.Applications.ApplicantAgent", "ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicantAttachment", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.Application", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", "Applicant") + .WithMany() + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.ApplicationForm", "ApplicationForm") + .WithMany() + .HasForeignKey("ApplicationFormId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.ApplicationStatus", "ApplicationStatus") + .WithMany("Applications") + .HasForeignKey("ApplicationStatusId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Applicant"); + + b.Navigation("ApplicationForm"); + + b.Navigation("ApplicationStatus"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationAssignment", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", "Application") + .WithMany("ApplicationAssignments") + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", "Assignee") + .WithMany() + .HasForeignKey("AssigneeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Application"); + + b.Navigation("Assignee"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationAttachment", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationChefsFileAttachment", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationContact", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationForm", b => + { + b.HasOne("Unity.GrantManager.Intakes.Intake", null) + .WithMany() + .HasForeignKey("IntakeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.ApplicationForm", null) + .WithMany() + .HasForeignKey("ParentFormId") + .OnDelete(DeleteBehavior.NoAction); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationFormSubmission", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Applications.ApplicationForm", null) + .WithMany() + .HasForeignKey("ApplicationFormId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationFormVersion", b => + { + b.HasOne("Unity.GrantManager.Applications.ApplicationForm", null) + .WithMany() + .HasForeignKey("ApplicationFormId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationLink", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany("ApplicationLinks") + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationTags", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", "Application") + .WithMany("ApplicationTags") + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.GlobalTag.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Application"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.AssessmentAttachment", b => + { + b.HasOne("Unity.GrantManager.Assessments.Assessment", null) + .WithMany() + .HasForeignKey("AssessmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.AuditHistory", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.FundingHistory", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.IssueTracking", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ReportsHistory", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId"); + }); + + modelBuilder.Entity("Unity.GrantManager.Assessments.Assessment", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", "Application") + .WithMany("Assessments") + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", null) + .WithMany() + .HasForeignKey("AssessorId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.ApplicantComment", b => + { + b.HasOne("Unity.GrantManager.Applications.Applicant", null) + .WithMany() + .HasForeignKey("ApplicantId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", null) + .WithMany() + .HasForeignKey("CommenterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.ApplicationComment", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", null) + .WithMany() + .HasForeignKey("CommenterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Comments.AssessmentComment", b => + { + b.HasOne("Unity.GrantManager.Assessments.Assessment", null) + .WithMany() + .HasForeignKey("AssessmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Identity.Person", null) + .WithMany() + .HasForeignKey("CommenterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Contacts.ContactLink", b => + { + b.HasOne("Unity.GrantManager.Contacts.Contact", null) + .WithMany() + .HasForeignKey("ContactId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.GrantManager.Notifications.ScheduledNotificationTracking", b => + { + b.HasOne("Unity.GrantManager.Applications.Application", null) + .WithMany() + .HasForeignKey("ApplicationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.Notifications.ScheduledNotification", null) + .WithMany() + .HasForeignKey("ScheduledNotificationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.Notifications.EmailGroups.EmailGroupUser", b => + { + b.HasOne("Unity.Notifications.EmailGroups.EmailGroup", null) + .WithMany() + .HasForeignKey("GroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Unity.Notifications.Emails.EmailLogAttachment", b => + { + b.HasOne("Unity.Notifications.Emails.EmailLog", null) + .WithMany() + .HasForeignKey("EmailLogId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Unity.Notifications.Templates.EmailTemplate", null) + .WithMany() + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.SubscriptionGroupSubscription", b => + { + b.HasOne("Unity.Notifications.Templates.SubscriptionGroup", "SubscriptionGroup") + .WithMany() + .HasForeignKey("GroupId"); + + b.HasOne("Unity.Notifications.Templates.Subscriber", "Subscriber") + .WithMany() + .HasForeignKey("SubscriberId"); + + b.Navigation("Subscriber"); + + b.Navigation("SubscriptionGroup"); + }); + + modelBuilder.Entity("Unity.Notifications.Templates.TriggerSubscription", b => + { + b.HasOne("Unity.Notifications.Templates.SubscriptionGroup", "SubscriptionGroup") + .WithMany() + .HasForeignKey("SubscriptionGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.Notifications.Templates.EmailTemplate", "EmailTemplate") + .WithMany() + .HasForeignKey("TemplateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.Notifications.Templates.Trigger", "Trigger") + .WithMany() + .HasForeignKey("TriggerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EmailTemplate"); + + b.Navigation("SubscriptionGroup"); + + b.Navigation("Trigger"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentRequests.ExpenseApproval", b => + { + b.HasOne("Unity.Payments.Domain.PaymentRequests.PaymentRequest", "PaymentRequest") + .WithMany("ExpenseApprovals") + .HasForeignKey("PaymentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PaymentRequest"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentRequests.PaymentRequest", b => + { + b.HasOne("Unity.Payments.Domain.AccountCodings.AccountCoding", "AccountCoding") + .WithMany() + .HasForeignKey("AccountCodingId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("Unity.Payments.Domain.Suppliers.Site", "Site") + .WithMany() + .HasForeignKey("SiteId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("AccountCoding"); + + b.Navigation("Site"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentTags.PaymentTag", b => + { + b.HasOne("Unity.Payments.Domain.PaymentRequests.PaymentRequest", null) + .WithMany("PaymentTags") + .HasForeignKey("PaymentRequestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Unity.GrantManager.GlobalTag.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.Suppliers.Site", b => + { + b.HasOne("Unity.Payments.Domain.Suppliers.Supplier", "Supplier") + .WithMany("Sites") + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.ScoresheetInstances.ScoresheetInstance", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Question", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.Scoresheet", b => + { + b.Navigation("Instances"); + + b.Navigation("Sections"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Scoresheets.ScoresheetSection", b => + { + b.Navigation("Fields"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.WorksheetInstances.WorksheetInstance", b => + { + b.Navigation("Values"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.Worksheet", b => + { + b.Navigation("Links"); + + b.Navigation("Sections"); + }); + + modelBuilder.Entity("Unity.Flex.Domain.Worksheets.WorksheetSection", b => + { + b.Navigation("Fields"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.Applicant", b => + { + b.Navigation("ApplicantAddresses"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.Application", b => + { + b.Navigation("ApplicantAddresses"); + + b.Navigation("ApplicantAgent"); + + b.Navigation("ApplicationAssignments"); + + b.Navigation("ApplicationLinks"); + + b.Navigation("ApplicationTags"); + + b.Navigation("Assessments"); + }); + + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationStatus", b => + { + b.Navigation("Applications"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.PaymentRequests.PaymentRequest", b => + { + b.Navigation("ExpenseApprovals"); + + b.Navigation("PaymentTags"); + }); + + modelBuilder.Entity("Unity.Payments.Domain.Suppliers.Supplier", b => + { + b.Navigation("Sites"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.cs new file mode 100644 index 0000000000..8e9ba72947 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260820184916_AB33327_MoveAIScoresheetAnswersToAISchema.cs @@ -0,0 +1,85 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Unity.GrantManager.Migrations.TenantMigrations +{ + /// + public partial class AB33327_MoveAIScoresheetAnswersToAISchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ApplicationScoresheetAnswers", + schema: "AI", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApplicationId = table.Column(type: "uuid", nullable: false), + Answers = table.Column(type: "jsonb", nullable: false), + TenantId = table.Column(type: "uuid", nullable: true), + ExtraProperties = table.Column(type: "text", nullable: false), + ConcurrencyStamp = table.Column(type: "character varying(40)", maxLength: 40, nullable: false), + CreationTime = table.Column(type: "timestamp without time zone", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: true), + LastModificationTime = table.Column(type: "timestamp without time zone", nullable: true), + LastModifierId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ApplicationScoresheetAnswers", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_ApplicationScoresheetAnswers_ApplicationId", + schema: "AI", + table: "ApplicationScoresheetAnswers", + column: "ApplicationId", + unique: true); + + // Carry existing AI scoring results across before the source column is dropped. + // EF cannot scaffold this; without it every stored result is lost. + migrationBuilder.Sql(@" + INSERT INTO ""AI"".""ApplicationScoresheetAnswers"" + (""Id"", ""ApplicationId"", ""Answers"", ""TenantId"", + ""CreationTime"", ""ExtraProperties"", ""ConcurrencyStamp"") + SELECT gen_random_uuid(), + a.""Id"", + a.""AIScoresheetAnswers"", + a.""TenantId"", + now() AT TIME ZONE 'utc', + '{}', + replace(gen_random_uuid()::text, '-', '') + FROM ""Applications"" a + WHERE a.""AIScoresheetAnswers"" IS NOT NULL; + "); + + migrationBuilder.DropColumn( + name: "AIScoresheetAnswers", + table: "Applications"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AIScoresheetAnswers", + table: "Applications", + type: "jsonb", + nullable: true); + + migrationBuilder.Sql(@" + UPDATE ""Applications"" a + SET ""AIScoresheetAnswers"" = s.""Answers"" + FROM ""AI"".""ApplicationScoresheetAnswers"" s + WHERE s.""ApplicationId"" = a.""Id""; + "); + + migrationBuilder.DropTable( + name: "ApplicationScoresheetAnswers", + schema: "AI"); + } + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/GrantTenantDbContextModelSnapshot.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/GrantTenantDbContextModelSnapshot.cs index 5c44b8ca35..f393ca6de0 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/GrantTenantDbContextModelSnapshot.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/GrantTenantDbContextModelSnapshot.cs @@ -1271,9 +1271,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AIAnalysis") .HasColumnType("text"); - b.Property("AIScoresheetAnswers") - .HasColumnType("jsonb"); - b.Property("Acquisition") .HasColumnType("text"); @@ -2112,6 +2109,58 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ApplicationLinks", (string)null); }); + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationScoresheetAnswers", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Answers") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("ApplicationId") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp without time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId") + .IsUnique(); + + b.ToTable("ApplicationScoresheetAnswers", "AI"); + }); + modelBuilder.Entity("Unity.GrantManager.Applications.ApplicationStatus", b => { b.Property("Id") diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/AssessmentScoresWidgetViewComponent.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/AssessmentScoresWidgetViewComponent.cs index 6a247c3520..7467d57daf 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/AssessmentScoresWidgetViewComponent.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/AssessmentScoresWidgetViewComponent.cs @@ -20,6 +20,7 @@ using System.Text.Json; using Unity.AI.Permissions; using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Features; using Volo.Abp.Settings; @@ -35,6 +36,7 @@ public class AssessmentScoresWidgetViewComponent(IAssessmentRepository assessmen IScoresheetInstanceRepository scoresheetInstanceRepository, IApplicationRepository applicationRepository, IApplicationFormRepository applicationFormRepository, + IRepository scoresheetAnswersRepository, IFeatureChecker featureChecker, IPermissionChecker permissionChecker) : AbpViewComponent { @@ -53,12 +55,15 @@ public async Task InvokeAsync(Guid assessmentId, Guid curr var tenantManualEnabled = await settingProvider.GetAsync(AISettings.ManualGenerationEnabled, defaultValue: false); // Parse AI scoresheet answers if available + var storedAiAnswers = await scoresheetAnswersRepository + .FindAsync(x => x.ApplicationId == assessment.ApplicationId); + Dictionary? aiAnswers = null; - if (!string.IsNullOrEmpty(application.AIScoresheetAnswers)) + if (!string.IsNullOrEmpty(storedAiAnswers?.Answers)) { try { - var aiAnswersJson = JsonDocument.Parse(application.AIScoresheetAnswers); + var aiAnswersJson = JsonDocument.Parse(storedAiAnswers.Answers); aiAnswers = []; foreach (var property in aiAnswersJson.RootElement.EnumerateObject()) { diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Assessments/AssessmentAppServiceTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Assessments/AssessmentAppServiceTests.cs index 59dcc1e6c5..b24a68d68e 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Assessments/AssessmentAppServiceTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Assessments/AssessmentAppServiceTests.cs @@ -60,8 +60,6 @@ public async Task CreateAsync_Should_Throw_On_Duplicate_Assessment() using var uow = _unitOfWorkManager.Begin(); var application = (await _applicationsRepository.GetListAsync())[0]; - var assessments = (await _assessmentRepository.GetQueryableAsync()).Where(s => s.ApplicationId == application.Id).ToList(); - var count = assessments.Count; // Act @@ -83,7 +81,7 @@ public async Task GetCurrentUserAssessmentId_Should_Return_Guid_On_Existing_User using var uow = _unitOfWorkManager.Begin(); var application = (await _applicationsRepository.GetListAsync())[0]; var assessment = (await _assessmentRepository.GetQueryableAsync()) - .Where(s => s.ApplicationId == application.Id && s.AssessorId == GrantManagerTestData.User1_UserId).First(); + .First(s => s.ApplicationId == application.Id && s.AssessorId == GrantManagerTestData.User1_UserId); // Act var assessmentId = await _assessmentAppService.GetCurrentUserAssessmentId(application.Id); @@ -126,7 +124,7 @@ public async Task GetAllPermittedActions_Returns_Assessment_Workflow_Actions_Lis using var uow = _unitOfWorkManager.Begin(); var assessment = (await _assessmentRepository.GetQueryableAsync()) - .Where(s => s.AssessorId == GrantManagerTestData.User1_UserId).First(); + .First(s => s.AssessorId == GrantManagerTestData.User1_UserId); // Act var assessmentActions = await _assessmentAppService.GetPermittedActions(assessment.Id); @@ -144,7 +142,7 @@ public async Task ExecuteAssessmentActions_Should_Execute_Valid_State_Transition using var uow = _unitOfWorkManager.Begin(); var assessment = (await _assessmentRepository.GetQueryableAsync()) - .Where(s => s.AssessorId == GrantManagerTestData.User1_UserId).First(); + .First(s => s.AssessorId == GrantManagerTestData.User1_UserId); assessment.ApprovalRecommended = true; // Act @@ -164,7 +162,7 @@ public async Task ExecuteAssessmentActions_Should_Fail_On_Invalid_State_Transiti using var uow = _unitOfWorkManager.Begin(); var assessment = (await _assessmentRepository.GetQueryableAsync()) - .Where(s => s.AssessorId == GrantManagerTestData.User1_UserId).First(); + .First(s => s.AssessorId == GrantManagerTestData.User1_UserId); // Assert await Assert.ThrowsAsync(async () => @@ -174,13 +172,13 @@ await _assessmentAppService.ExecuteAssessmentAction(assessment.Id, AssessmentAct [Fact] [Trait("Category", "Integration")] - public async Task CloneFromAiAsync_Should_Throw_On_Non_AI_Assessment() - { - // Arrange - Login(GrantManagerTestData.User1_UserId); - SetFeatureEnabled("Unity.AI.Scoring", true); - - using var uow = _unitOfWorkManager.Begin(); + public async Task CloneFromAiAsync_Should_Throw_On_Non_AI_Assessment() + { + // Arrange + Login(GrantManagerTestData.User1_UserId); + SetFeatureEnabled("Unity.AI.Scoring", true); + + using var uow = _unitOfWorkManager.Begin(); // Act & Assert — Assessment1_Id is a human assessment, not AI await Assert.ThrowsAsync(async () => @@ -189,13 +187,13 @@ await Assert.ThrowsAsync(async () => [Fact] [Trait("Category", "Integration")] - public async Task CloneFromAiAsync_Should_Create_Human_Assessment_From_AI_Assessment() - { - // Arrange — User2 has no existing human assessment on Application1 - Login(GrantManagerTestData.User2_UserId); - SetFeatureEnabled("Unity.AI.Scoring", true); - - using var uow = _unitOfWorkManager.Begin(); + public async Task CloneFromAiAsync_Should_Create_Human_Assessment_From_AI_Assessment() + { + // Arrange — User2 has no existing human assessment on Application1 + Login(GrantManagerTestData.User2_UserId); + SetFeatureEnabled("Unity.AI.Scoring", true); + + using var uow = _unitOfWorkManager.Begin(); var beforeCount = (await _assessmentRepository.GetQueryableAsync()) .Count(s => s.ApplicationId == GrantManagerTestData.Application1_Id && !s.IsAiAssessment); @@ -212,13 +210,13 @@ public async Task CloneFromAiAsync_Should_Create_Human_Assessment_From_AI_Assess [Fact] [Trait("Category", "Integration")] - public async Task GetDisplayList_Should_Exclude_AI_Assessment_When_Feature_Disabled() - { - // Arrange — Unity.AI.Scoring feature is disabled by default in test environment - Login(GrantManagerTestData.User1_UserId); - SetFeatureEnabled("Unity.AI.Scoring", false); - - using var uow = _unitOfWorkManager.Begin(); + public async Task GetDisplayList_Should_Exclude_AI_Assessment_When_Feature_Disabled() + { + // Arrange — Unity.AI.Scoring feature is disabled by default in test environment + Login(GrantManagerTestData.User1_UserId); + SetFeatureEnabled("Unity.AI.Scoring", false); + + using var uow = _unitOfWorkManager.Begin(); // Act var result = await _assessmentAppService.GetDisplayList(GrantManagerTestData.Application1_Id); diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantApplications/Automation/ApplicationScoresheetAnswersPersistenceTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantApplications/Automation/ApplicationScoresheetAnswersPersistenceTests.cs new file mode 100644 index 0000000000..d28cbed39c --- /dev/null +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantApplications/Automation/ApplicationScoresheetAnswersPersistenceTests.cs @@ -0,0 +1,71 @@ +using Shouldly; +using System; +using System.Linq; +using System.Threading.Tasks; +using Unity.GrantManager.Applications; +using Unity.GrantManager.GrantApplications.Automation.BackgroundJobs; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Guids; +using Volo.Abp.Uow; +using Xunit; +using Xunit.Abstractions; + +namespace Unity.GrantManager.GrantApplications.Automation +{ + /// + /// AI scoring results moved off the Application aggregate into AI.ApplicationScoresheetAnswers. + /// These cover the upsert behaviour that replaced the old direct column assignment. + /// + public class ApplicationScoresheetAnswersPersistenceTests : GrantManagerApplicationTestBase + { + private readonly IRepository _scoresheetAnswersRepository; + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IGuidGenerator _guidGenerator; + + public ApplicationScoresheetAnswersPersistenceTests(ITestOutputHelper outputHelper) : base(outputHelper) + { + _scoresheetAnswersRepository = GetRequiredService>(); + _unitOfWorkManager = GetRequiredService(); + _guidGenerator = GetRequiredService(); + } + + [Fact] + [Trait("Category", "Integration")] + public async Task Should_InsertAnswers_When_NoneStoredForApplication() + { + var applicationId = _guidGenerator.Create(); + const string answers = """{"11111111-1111-1111-1111-111111111111":{"answer":"3"}}"""; + + await AIGenerationRequestJobHelper.SaveScoresheetAnswersInNewUowAsync( + _unitOfWorkManager, _scoresheetAnswersRepository, _guidGenerator, applicationId, answers); + + using var uow = _unitOfWorkManager.Begin(); + var stored = await _scoresheetAnswersRepository.FindAsync(x => x.ApplicationId == applicationId); + + stored.ShouldNotBeNull(); + stored.Answers.ShouldBe(answers); + } + + [Fact] + [Trait("Category", "Integration")] + public async Task Should_UpdateExistingRow_When_ScoringRegeneratedForSameApplication() + { + var applicationId = _guidGenerator.Create(); + const string firstAnswers = """{"11111111-1111-1111-1111-111111111111":{"answer":"3"}}"""; + const string secondAnswers = """{"11111111-1111-1111-1111-111111111111":{"answer":"5"}}"""; + + await AIGenerationRequestJobHelper.SaveScoresheetAnswersInNewUowAsync( + _unitOfWorkManager, _scoresheetAnswersRepository, _guidGenerator, applicationId, firstAnswers); + await AIGenerationRequestJobHelper.SaveScoresheetAnswersInNewUowAsync( + _unitOfWorkManager, _scoresheetAnswersRepository, _guidGenerator, applicationId, secondAnswers); + + using var uow = _unitOfWorkManager.Begin(); + var rows = (await _scoresheetAnswersRepository.GetQueryableAsync()) + .Where(x => x.ApplicationId == applicationId) + .ToList(); + + rows.Count.ShouldBe(1); + rows[0].Answers.ShouldBe(secondAnswers); + } + } +} diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Web.Tests/Components/AssessmentScoresWidgetTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Web.Tests/Components/AssessmentScoresWidgetTests.cs index c96b8c4e51..671d61acb8 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Web.Tests/Components/AssessmentScoresWidgetTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Web.Tests/Components/AssessmentScoresWidgetTests.cs @@ -4,6 +4,8 @@ using NSubstitute; using Shouldly; using System; +using System.Linq.Expressions; +using System.Threading; using System.Threading.Tasks; using Unity.AI.Permissions; using Unity.Flex.Domain.ScoresheetInstances; @@ -13,6 +15,7 @@ using Unity.GrantManager.Web.Views.Shared.Components.AssessmentScoresWidget; using Volo.Abp.Authorization.Permissions; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; using Volo.Abp.Features; using Volo.Abp.Settings; using Xunit; @@ -52,10 +55,12 @@ public async Task AssessmentScoresWidgetReturnsStatus() CleanGrowth = expectedCleanGrowth })); - applicationRepository.GetAsync(applicationId).Returns(Task.FromResult(new Application() - { - AIScoresheetAnswers = null - })); + applicationRepository.GetAsync(applicationId).Returns(Task.FromResult(new Application())); + + var scoresheetAnswersRepository = Substitute.For>(); + scoresheetAnswersRepository + .FindAsync(Arg.Any>>(), Arg.Any(), Arg.Any()) + .Returns(Task.FromResult(null)); applicationFormRepository.GetAsync(Arg.Any()).Returns(Task.FromResult(new ApplicationForm())); settingProvider.GetOrNullAsync(Arg.Any()).Returns(Task.FromResult("false")); @@ -80,6 +85,7 @@ public async Task AssessmentScoresWidgetReturnsStatus() instanceRepository, applicationRepository, applicationFormRepository, + scoresheetAnswersRepository, featureChecker, permissionChecker) { @@ -100,6 +106,61 @@ public async Task AssessmentScoresWidgetReturnsStatus() resultModel!.CleanGrowth.ShouldBe(expectedCleanGrowth); } + [Fact] + public async Task Should_ReturnModel_When_StoredAiAnswersAreMalformed() + { + var assessmentId = Guid.NewGuid(); + var applicationId = Guid.NewGuid(); + + var assessmentRepository = Substitute.For(); + var applicationRepository = Substitute.For(); + var applicationFormRepository = Substitute.For(); + var instanceRepository = Substitute.For(); + var featureChecker = Substitute.For(); + var permissionChecker = Substitute.For(); + var settingProvider = Substitute.For(); + var lazyServiceProvider = Substitute.For(); + + assessmentRepository.GetAsync(assessmentId).Returns(Task.FromResult( + new Assessment(id: assessmentId, applicationId: applicationId, assessorId: Guid.NewGuid()))); + applicationRepository.GetAsync(applicationId).Returns(Task.FromResult(new Application())); + applicationFormRepository.GetAsync(Arg.Any()).Returns(Task.FromResult(new ApplicationForm())); + instanceRepository.GetByCorrelationAsync(assessmentId).Returns(Task.FromResult(null)); + settingProvider.GetOrNullAsync(Arg.Any()).Returns(Task.FromResult("false")); + lazyServiceProvider.LazyGetRequiredService().Returns(settingProvider); + + var scoresheetAnswersRepository = Substitute.For>(); + scoresheetAnswersRepository + .FindAsync(Arg.Any>>(), Arg.Any(), Arg.Any()) + .Returns(Task.FromResult( + new ApplicationScoresheetAnswers(Guid.NewGuid(), applicationId, "not-json"))); + + var viewComponent = new AssessmentScoresWidgetViewComponent( + assessmentRepository, + Substitute.For(), + instanceRepository, + applicationRepository, + applicationFormRepository, + scoresheetAnswersRepository, + featureChecker, + permissionChecker) + { + ViewComponentContext = new ViewComponentContext + { + ViewContext = new ViewContext { HttpContext = new DefaultHttpContext() } + }, + LazyServiceProvider = lazyServiceProvider + }; + + var result = await viewComponent.InvokeAsync(assessmentId, Guid.NewGuid()) as ViewViewComponentResult; + + var model = result!.ViewData!.Model as AssessmentScoresWidgetViewModel; + model.ShouldNotBeNull(); + model.AssessmentId.ShouldBe(assessmentId); + await scoresheetAnswersRepository.Received(1).FindAsync( + Arg.Any>>(), Arg.Any(), Arg.Any()); + } + [Fact] public async Task Should_ShowGenerateButton_When_AllGuardsPassed() { @@ -161,6 +222,11 @@ public async Task Should_HideGenerateButton_When_UserLacksGeneratePermission() var instanceRepository = Substitute.For(); instanceRepository.GetByCorrelationAsync(assessmentId).Returns(Task.FromResult(null)); + var scoresheetAnswersRepository = Substitute.For>(); + scoresheetAnswersRepository + .FindAsync(Arg.Any>>(), Arg.Any(), Arg.Any()) + .Returns(Task.FromResult(null)); + var viewComponentContext = new ViewComponentContext { ViewContext = new ViewContext { HttpContext = new DefaultHttpContext() } @@ -172,6 +238,7 @@ public async Task Should_HideGenerateButton_When_UserLacksGeneratePermission() instanceRepository, applicationRepository, applicationFormRepository, + scoresheetAnswersRepository, featureChecker, permissionChecker) {