From 7a34319f54b14be3e61bb4e03984f66194dc87b0 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 8 May 2026 10:33:08 -0700 Subject: [PATCH 01/10] AB#32591 persist scoresheet panel state --- .../Pages/GrantApplications/Details.js | 45 ++++++++- .../AssessmentScoresWidget/Default.js | 91 +++++++++++++++++++ 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js index 72b1bd1b33..77a7ab73ed 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js @@ -309,19 +309,51 @@ $(function () { wrapper: '#assessmentScoresWidgetArea', filterCallback: function () { return { - assessmentId: decodeURIComponent($('#AssessmentId').val()), + assessmentId: + selectedReviewDetails?.id || + decodeURIComponent($('#AssessmentId').val()), currentUserId: decodeURIComponent(abp.currentUser.id), }; }, }); + let assessmentScoresRefreshToken = 0; + function getAssessmentScoresWidgetElement() { + return document.getElementById('assessmentScoresWidgetArea'); + } + + function refreshAssessmentScoresWidget() { + const refreshToken = ++assessmentScoresRefreshToken; + globalThis.saveAssessmentScoresWidgetState?.( + getAssessmentScoresWidgetElement() + ); + + const refreshResult = assessmentScoresWidgetManager.refresh(); + + const afterRefresh = () => { + if (refreshToken !== assessmentScoresRefreshToken) { + return; + } + + updateSubtotal(); + }; + + if (refreshResult && typeof refreshResult.then === 'function') { + refreshResult.then(afterRefresh); + } else { + setTimeout(afterRefresh, 0); + } + } + PubSub.subscribe('refresh_assessment_scores', (msg, data) => { - assessmentScoresWidgetManager.refresh(); - updateSubtotal(); + refreshAssessmentScoresWidget(); }); PubSub.subscribe('select_application_review', (msg, data) => { if (data) { + globalThis.saveAssessmentScoresWidgetState?.( + getAssessmentScoresWidgetElement() + ); selectedReviewDetails = data; setDetailsContext('assessment'); let selectElement = document.getElementById( @@ -332,8 +364,7 @@ $(function () { review: selectedReviewDetails, }); assessmentUserDetailsWidgetManager.refresh(); - assessmentScoresWidgetManager.refresh(); - updateSubtotal(); + refreshAssessmentScoresWidget(); checkCurrentUser(data); } else { setDetailsContext('application'); @@ -341,6 +372,10 @@ $(function () { }); PubSub.subscribe('deselect_application_review', (msg, data) => { + globalThis.saveAssessmentScoresWidgetState?.( + getAssessmentScoresWidgetElement() + ); + assessmentScoresRefreshToken++; setDetailsContext('application'); }); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.js index 23f509ab45..ea5ac1311a 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.js @@ -1,3 +1,94 @@ +const assessmentScoresWidgetPanelStates = {}; + +function getAssessmentScoresScrollContainer(wrapper) { + return ( + wrapper.closest('.details-scrollable') || + document.getElementById('detailsTabContent') || + document.documentElement + ); +} + +function restoreAssessmentScoresScrollPosition(wrapper, scrollTop) { + if (!Number.isFinite(scrollTop)) { + return; + } + + const container = getAssessmentScoresScrollContainer(wrapper); + const maxScroll = Math.max( + 0, + container.scrollHeight - container.clientHeight + ); + container.scrollTop = Math.min(scrollTop, maxScroll); +} + +function getAssessmentScoresWidgetStateKey(wrapper) { + return wrapper.querySelector('#AssessmentId')?.value; +} + +function saveAssessmentScoresWidgetState(wrapper) { + if (!wrapper) { + return; + } + + const key = getAssessmentScoresWidgetStateKey(wrapper); + if (!key) { + return; + } + + const scrollContainer = getAssessmentScoresScrollContainer(wrapper); + const scrollTop = scrollContainer.scrollTop; + const expandedCollapseIds = Array.from( + wrapper.querySelectorAll( + '#assessment-scoresheet .accordion-collapse.show' + ) + ) + .map((accordion) => accordion.id) + .filter(Boolean); + + assessmentScoresWidgetPanelStates[key] = { + expandedCollapseIds, + scrollTop, + }; +} + +function restoreAssessmentScoresWidgetState(wrapper) { + const key = getAssessmentScoresWidgetStateKey(wrapper); + const state = assessmentScoresWidgetPanelStates[key]; + if (!state) { + restoreAssessmentScoresScrollPosition(wrapper, 0); + return; + } + + state.expandedCollapseIds.forEach((id) => { + const accordion = document.getElementById(id); + if (!accordion || !wrapper.contains(accordion)) { + return; + } + + accordion.classList.add('show'); + const accordionButton = accordion.previousElementSibling?.querySelector( + '.accordion-button' + ); + accordionButton?.classList.remove('collapsed'); + accordionButton?.setAttribute('aria-expanded', 'true'); + }); + + requestAnimationFrame(() => + restoreAssessmentScoresScrollPosition(wrapper, state.scrollTop) + ); +} + +globalThis.saveAssessmentScoresWidgetState = saveAssessmentScoresWidgetState; + +abp.widgets.AssessmentScoresWidget = function ($wrapper) { + return { + init: function () { + restoreAssessmentScoresWidgetState($wrapper[0]); + updateSubtotal(); + }, + }; +}; + function saveScoresSection(formId, sectionId) { const assessmentId = $('#AssessmentId').val(); const secSaveButton = document.getElementById( From 1f434bf3b4ca7445399b26f408748c3a1954e2f9 Mon Sep 17 00:00:00 2001 From: aurelio-aot Date: Tue, 12 May 2026 14:05:56 -0700 Subject: [PATCH 02/10] AB#31762: Remove database columns not already being used --- .../ApplicationForms/ApplicationFormDto.cs | 1 - .../CreateUpdateApplicationFormDto.cs | 1 - .../Applications/ApplicationForm.cs | 1 - .../Applications/ApplicationFormSubmission.cs | 1 - ...deredHtmlAndRenderFormIoToHtml.Designer.cs | 5032 +++++++++++++++++ ...RemoveRenderedHtmlAndRenderFormIoToHtml.cs | 39 + .../GrantTenantDbContextModelSnapshot.cs | 6 - .../Controllers/FormController.cs | 54 - .../Mapping/GrantManagerWebMapperlyProfile.cs | 2 - .../Pages/GrantApplications/Details.cshtml | 34 +- .../Pages/GrantApplications/Details.cshtml.cs | 15 - .../Pages/GrantApplications/Details.js | 51 +- 12 files changed, 5088 insertions(+), 149 deletions(-) create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.Designer.cs create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.cs diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/ApplicationFormDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/ApplicationFormDto.cs index 866085f414..02d5f484e5 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/ApplicationFormDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/ApplicationFormDto.cs @@ -23,7 +23,6 @@ public class ApplicationFormDto : EntityDto public bool Payable { get; set; } public bool PreventPayment { get; set; } public Guid? AccountCodingId { get; set; } - public bool RenderFormIoToHtml { get; set; } public Guid? ScoresheetId { get; set; } public Guid? TenantId { get; set; } public bool IsDirectApproval { get; set; } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/CreateUpdateApplicationFormDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/CreateUpdateApplicationFormDto.cs index 1465b4268d..cacd837d78 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/CreateUpdateApplicationFormDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicationForms/CreateUpdateApplicationFormDto.cs @@ -13,6 +13,5 @@ public class CreateUpdateApplicationFormDto public string? Category { get; set; } public int? Version { get; set; } public bool Payable { get; set; } - public bool RenderFormIoToHtml { get; set; } } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationForm.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationForm.cs index d4377ae09e..a2644e9363 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationForm.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationForm.cs @@ -31,7 +31,6 @@ public class ApplicationForm : FullAuditedAggregateRoot, IMultiTenant public int? DefaultPaymentGroup { get; set; } public FormHierarchyType? FormHierarchy { get; set; } public Guid? ParentFormId { get; set; } - public bool RenderFormIoToHtml { get; set; } = false; public bool IsDirectApproval { get; set; } = false; public bool AutomaticallyGenerateAIAnalysis { get; set; } = false; public bool ManuallyInitiateAIAnalysis { get; set; } = false; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationFormSubmission.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationFormSubmission.cs index eb5b9de4c0..5ea68756d1 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationFormSubmission.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Applications/ApplicationFormSubmission.cs @@ -28,7 +28,6 @@ public Guid ApplicationId [Column(TypeName = "jsonb")] public string Submission { get; set; } = string.Empty; public Guid? TenantId { get; set; } - public string? RenderedHTML { get; set; } = string.Empty; public Guid? FormVersionId { get; set; } [Column(TypeName = "jsonb")] public string ReportData { get; set; } = "{}"; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.Designer.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.Designer.cs new file mode 100644 index 0000000000..fc6368829d --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.Designer.cs @@ -0,0 +1,5032 @@ +// +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("20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml")] + partial class RemoveRenderedHtmlAndRenderFormIoToHtml + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "10.0.3") + .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("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.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("ExtraProperties") + .IsRequired() + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FiscalDay") + .HasColumnType("integer"); + + b.Property("FiscalMonth") + .HasColumnType("text"); + + b.Property("FundingHistoryComments") + .HasColumnType("text"); + + b.Property("IndigenousOrgInd") + .HasColumnType("text"); + + 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("OrganizationSize") + .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("TenantId"); + + 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("AIScoresheetAnswers") + .HasColumnType("jsonb"); + + 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("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("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.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("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("AuditTrackingNumber") + .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("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("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.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("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("RetryAttempts") + .HasColumnType("integer"); + + 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("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("EmailLogId"); + + b.HasIndex("S3ObjectKey"); + + b.ToTable("EmailLogAttachments", "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("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("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("FsbNotificationEmailLogId"); + + b.HasIndex("ReferenceNumber") + .IsUnique(); + + b.HasIndex("SiteId"); + + 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.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) + .IsRequired(); + }); + + 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) + .IsRequired(); + + 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/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.cs new file mode 100644 index 0000000000..fca59ceb81 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260512205529_RemoveRenderedHtmlAndRenderFormIoToHtml.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Unity.GrantManager.Migrations.TenantMigrations +{ + /// + public partial class RemoveRenderedHtmlAndRenderFormIoToHtml : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "RenderedHTML", + table: "ApplicationFormSubmissions"); + + migrationBuilder.DropColumn( + name: "RenderFormIoToHtml", + table: "ApplicationForms"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "RenderedHTML", + table: "ApplicationFormSubmissions", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "RenderFormIoToHtml", + table: "ApplicationForms", + type: "boolean", + nullable: false, + defaultValue: false); + } + } +} 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 cff23c144c..c97b21dda4 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 @@ -1772,9 +1772,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PreventPayment") .HasColumnType("boolean"); - b.Property("RenderFormIoToHtml") - .HasColumnType("boolean"); - b.Property("ScoresheetId") .HasColumnType("uuid"); @@ -1856,9 +1853,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b.Property("RenderedHTML") - .HasColumnType("text"); - b.Property("ReportData") .IsRequired() .HasColumnType("jsonb"); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.HttpApi/Controllers/FormController.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.HttpApi/Controllers/FormController.cs index 5a90cbd0a5..efcf1ae83a 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.HttpApi/Controllers/FormController.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.HttpApi/Controllers/FormController.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc; using System; using System.Linq; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Unity.GrantManager.ApplicationForms; using Unity.GrantManager.Applications; @@ -17,13 +16,11 @@ namespace Unity.GrantManager.Controllers [Route("/api/app")] public partial class FormController( IApplicationFormRepository applicationFormRepository, - IApplicationFormSubmissionRepository applicationFormSubmissionRepository, IApplicationFormVersionAppService applicationFormVersionAppService, IFormsApiService formsApiService) : AbpController { private readonly IApplicationFormRepository _applicationFormRepository = applicationFormRepository; private readonly IApplicationFormVersionAppService _applicationFormVersionAppService = applicationFormVersionAppService; - private readonly IApplicationFormSubmissionRepository _applicationFormSubmissionRepository = applicationFormSubmissionRepository; private readonly IFormsApiService _formsApiService = formsApiService; [HttpPost("form/{formId}/version/{formVersionId}")] @@ -99,56 +96,5 @@ public async Task SynchronizeChefsAvailableFields(string formId, return formName; } - public class ApplicationSubmission - { - public string InnerHTML { set; get; } = string.Empty; - public string SubmissionId { set; get; } = string.Empty; - } - - [HttpPost] - [Route("/api/app/submission")] - [Consumes("application/json")] - public async Task StoreSubmissionHtml([FromBody] ApplicationSubmission applicationSubmission) - { - if (!ModelState.IsValid) - { - Logger.LogWarning("Invalid model state for StoreSubmissionHtml"); - return BadRequest(ModelState); - } - - try - { - var applicationFormSubmission = await _applicationFormSubmissionRepository.GetAsync(Guid.Parse(applicationSubmission.SubmissionId)); - - if (applicationFormSubmission == null) - { - return NotFound("Submission not found."); - } - - // Format HTML - applicationFormSubmission.RenderedHTML = FormatHtml(applicationSubmission.InnerHTML); - - await _applicationFormSubmissionRepository.UpdateAsync(applicationFormSubmission); - return Ok("Submission updated successfully."); - } - catch (Exception ex) - { - string ExceptionMessage = ex.Message; - Logger.LogError(ex, "FormController->StoreSubmissionHtml: {ExceptionMessage}", ExceptionMessage); - return StatusCode(500, "An error occurred while processing your request."); - } - } - - [GeneratedRegex(@"\s+")] - private static partial Regex WhitespaceRegex(); - - public static string FormatHtml(string html) - { - // Use the generated regex to replace sequences of whitespace characters with a single space - string formattedInnerHTML = WhitespaceRegex().Replace(html, " "); - - // Remove new lines and tabs - return formattedInnerHTML.Replace(Environment.NewLine, "").Replace("\t", " "); - } } } \ No newline at end of file diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Mapping/GrantManagerWebMapperlyProfile.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Mapping/GrantManagerWebMapperlyProfile.cs index 871ed14291..220e1026f7 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Mapping/GrantManagerWebMapperlyProfile.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Mapping/GrantManagerWebMapperlyProfile.cs @@ -34,13 +34,11 @@ public partial class CreateUpdateApplicationFormViewModelToDtoMapper : MapperBas [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.ChefsCriteriaFormGuid))] [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.Version))] [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.Payable))] - [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.RenderFormIoToHtml))] public override partial CreateUpdateApplicationFormDto Map(CreateUpdateApplicationFormViewModel source); [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.ChefsCriteriaFormGuid))] [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.Version))] [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.Payable))] - [MapperIgnoreTarget(nameof(CreateUpdateApplicationFormDto.RenderFormIoToHtml))] public override partial void Map(CreateUpdateApplicationFormViewModel source, CreateUpdateApplicationFormDto destination); } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml index 13133fe399..cb84a960a4 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml @@ -50,19 +50,19 @@ && formManualEnabled && await PermissionChecker.IsGrantedAsync(AIPermissions.Analysis.GenerateApplicationAnalysis); } -@section styles -{ - - -} -@section scripts -{ - - - - - -} +@section styles +{ + + +} +@section scripts +{ + + + + + +}
@await Component.InvokeAsync("ApplicationBreadcrumbWidget", new { applicationId = @Model.ApplicationId }) @@ -76,14 +76,12 @@ - - @@ -122,7 +120,7 @@ -
@Html.Raw(@Model.ApplicationFormSubmissionHtml)
+
@@ -287,7 +285,7 @@ } - +
@@ -507,7 +505,7 @@
} @*-------- AI Analysis Tab Section END ---------*@ -
+ diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml.cs index 151093e328..f7264249c6 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.cshtml.cs @@ -68,15 +68,6 @@ public class DetailsModel : AbpPageModel [BindProperty(SupportsGet = true)] public string? ApplicationFormSchema { get; set; } = null; - [BindProperty(SupportsGet = true)] - public string? ApplicationFormSubmissionHtml { get; set; } = null; - - [BindProperty(SupportsGet = true)] - public bool? HasRenderedHTML { get; set; } = false; - - [BindProperty(SupportsGet = true)] - public bool RenderFormIoToHtml { get; set; } = false; - [BindProperty(SupportsGet = true)] public Guid? CurrentUserId { get; set; } @@ -151,16 +142,10 @@ public async Task OnGetAsync() ApplicationFormId = applicationFormSubmission.ApplicationFormId; ChefsSubmissionId = applicationFormSubmission.ChefsSubmissionGuid; ApplicationFormSubmissionId = applicationFormSubmission.Id.ToString(); - HasRenderedHTML = !string.IsNullOrEmpty(applicationFormSubmission.RenderedHTML); ApplicationForm? applicationForm = await _grantApplicationAppService.GetApplicationFormAsync(ApplicationFormId); ArgumentNullException.ThrowIfNull(applicationForm); ApplicationScoresheetSchemaJson = await GetApplicationScoresheetSchemaJsonAsync(applicationForm); - RenderFormIoToHtml = applicationForm.RenderFormIoToHtml; ApplicationFormSubmissionData = applicationFormSubmission.Submission; - if (!string.IsNullOrEmpty(applicationFormSubmission.RenderedHTML) && RenderFormIoToHtml) - { - ApplicationFormSubmissionHtml = applicationFormSubmission.RenderedHTML; - } } public async Task OnPostAsync() diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js index 72b1bd1b33..e173a35970 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Details.js @@ -4,9 +4,6 @@ */ $(function () { let selectedReviewDetails = null; - let renderFormIoToHtml = - document.getElementById('RenderFormIoToHtml').value; - let hasRenderedHtml = document.getElementById('HasRenderedHTML').value; abp.localization.getResource('GrantManager'); const divider = document.getElementById('main-divider'); @@ -92,24 +89,8 @@ $(function () { } function renderSubmission() { - // Initialize shadow DOM first const shadowRoot = initializeShadowDOM(); - - if (renderFormIoToHtml == 'False' || hasRenderedHtml == 'False') { - getSubmission(shadowRoot); - } else { - $('.spinner-grow').hide(); - - // Inject pre-rendered HTML into shadow DOM - if (shadowRoot) { - const htmlContent = document.getElementById('ApplicationFormSubmissionHtml'); - if (htmlContent?.value) { - shadowRoot.innerHTML += DOMPurify.sanitize(htmlContent.value); - } - } - - addEventListeners(shadowRoot); - } + getSubmission(shadowRoot); } @@ -171,36 +152,6 @@ $(function () { form.refresh(); form.on('render', () => addEventListeners(shadowRoot)); - waitFor(() => isFormChanging(form)).then(() => { - setTimeout(storeRenderedHtml, 2000); - }); - } - - async function storeRenderedHtml() { - if (renderFormIoToHtml == 'False') { - return; - } - const formioContainer = document.getElementById('formio'); - const shadowRoot = formioContainer.shadowRoot; - let innerHTML = shadowRoot ? shadowRoot.innerHTML : formioContainer.innerHTML; - let submissionId = document.getElementById( - 'ApplicationFormSubmissionId' - ).value; - $.ajax({ - url: '/api/app/submission', - data: JSON.stringify({ - SubmissionId: submissionId, - InnerHTML: innerHTML, - }), - contentType: 'application/json', - type: 'POST', - success: function (data) { - console.log(data); - }, - error: function () { - console.log('error'); - }, - }); } // Wait for the DOM to be fully loaded From 913b4f5fe60ce2f044e56c35964aaba36b58cb2e Mon Sep 17 00:00:00 2001 From: Andre Goncalves Date: Wed, 13 May 2026 10:46:42 -0700 Subject: [PATCH 03/10] AB#32683 - address stricter .net10 json handling --- .../ApplicantProfile/ProfileData/ApplicantAddressInfoDto.cs | 2 ++ .../ApplicantProfile/ProfileData/ApplicantContactInfoDto.cs | 2 ++ .../ApplicantProfile/ProfileData/ApplicantOrgInfoDto.cs | 2 ++ .../ApplicantProfile/ProfileData/ApplicantPaymentInfoDto.cs | 2 ++ .../ApplicantProfile/ProfileData/ApplicantProfileDataDto.cs | 1 + .../ApplicantProfile/ProfileData/ApplicantSubmissionInfoDto.cs | 2 ++ 6 files changed, 11 insertions(+) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantAddressInfoDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantAddressInfoDto.cs index f7b956aba2..eebebab7b0 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantAddressInfoDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantAddressInfoDto.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Unity.GrantManager.ApplicantProfile.ProfileData { public class ApplicantAddressInfoDto : ApplicantProfileDataDto { + [JsonIgnore] public override string DataType => "ADDRESSINFO"; public List Addresses { get; set; } = []; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantContactInfoDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantContactInfoDto.cs index 716f78928c..93491dcd06 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantContactInfoDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantContactInfoDto.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Unity.GrantManager.ApplicantProfile.ProfileData { public class ApplicantContactInfoDto : ApplicantProfileDataDto { + [JsonIgnore] public override string DataType => "CONTACTINFO"; public List Contacts { get; set; } = []; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantOrgInfoDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantOrgInfoDto.cs index 4ca718a427..a962903493 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantOrgInfoDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantOrgInfoDto.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Unity.GrantManager.ApplicantProfile.ProfileData { public class ApplicantOrgInfoDto : ApplicantProfileDataDto { + [JsonIgnore] public override string DataType => "ORGINFO"; public List Organizations { get; set; } = []; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantPaymentInfoDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantPaymentInfoDto.cs index 3f4b70a7fc..a2744f7858 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantPaymentInfoDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantPaymentInfoDto.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Unity.GrantManager.ApplicantProfile.ProfileData { public class ApplicantPaymentInfoDto : ApplicantProfileDataDto { + [JsonIgnore] public override string DataType => "PAYMENTINFO"; public List Payments { get; set; } = []; diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantProfileDataDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantProfileDataDto.cs index 65da1a0fac..56a6b4e3ba 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantProfileDataDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantProfileDataDto.cs @@ -10,6 +10,7 @@ namespace Unity.GrantManager.ApplicantProfile.ProfileData [JsonDerivedType(typeof(ApplicantPaymentInfoDto), "PAYMENTINFO")] public class ApplicantProfileDataDto { + [JsonIgnore] public virtual string DataType { get; } = ""; } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantSubmissionInfoDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantSubmissionInfoDto.cs index 9c1fc36c73..91d130e171 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantSubmissionInfoDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ApplicantSubmissionInfoDto.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Unity.GrantManager.ApplicantProfile.ProfileData { public class ApplicantSubmissionInfoDto : ApplicantProfileDataDto { + [JsonIgnore] public override string DataType => "SUBMISSIONINFO"; public List Submissions { get; set; } = []; From bfad74a4b77c53a492371d1f15ccf0141f1946fa Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 13 May 2026 10:18:23 -0700 Subject: [PATCH 04/10] AB#32898 fix AI JSON defaults readonly initialization --- .../src/Unity.AI.Application/AI/Runtime/AIJsonDefaults.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIJsonDefaults.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIJsonDefaults.cs index 29e2e0ee30..1b322a5228 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIJsonDefaults.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/AIJsonDefaults.cs @@ -14,7 +14,7 @@ internal static class AIJsonDefaults static AIJsonDefaults() { - Indented.MakeReadOnly(); - IndentedCamelCase.MakeReadOnly(); + Indented.MakeReadOnly(populateMissingResolver: true); + IndentedCamelCase.MakeReadOnly(populateMissingResolver: true); } } From c3aca037cf52d3291bc365f7645f8b85b6b28c06 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 13 May 2026 10:19:56 -0700 Subject: [PATCH 05/10] AB#32290 fix AI cooldown endpoint wiring --- .../Unity.AI.Application/RateLimit/AIRateLimitAppService.cs | 3 +++ .../Pages/GrantApplications/ai-rate-limit.js | 2 +- .../Shared/Components/AssessmentScoresWidget/Default.cshtml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/RateLimit/AIRateLimitAppService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/RateLimit/AIRateLimitAppService.cs index a8d018a369..15781bb8b1 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/RateLimit/AIRateLimitAppService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/RateLimit/AIRateLimitAppService.cs @@ -1,11 +1,14 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; namespace Unity.AI.RateLimit; [Authorize] +[Route("api/app/ai/rate-limit")] public class AIRateLimitAppService(IAIRateLimiter rateLimiter) : AIAppService, IAIRateLimitAppService { + [HttpGet("state")] public virtual Task GetStateAsync() => rateLimiter.GetStateAsync(); } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/ai-rate-limit.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/ai-rate-limit.js index ad5a7221d9..f47e7d7ba9 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/ai-rate-limit.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/ai-rate-limit.js @@ -88,7 +88,7 @@ if (!force && now - lastFetchAt < 1000) return; lastFetchAt = now; try { - const res = await fetch('/api/app/ai-rate-limit/state', { + const res = await fetch('/api/app/ai/rate-limit/state', { credentials: 'same-origin', headers: { Accept: 'application/json' }, }); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.cshtml index f630006bea..2a0c5a69ad 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/AssessmentScoresWidget/Default.cshtml @@ -26,7 +26,7 @@
@if (Model.IsAIScoringEnabled && Model.IsAiAssessment) { - + + + +
+ +
+
+ @await Component.InvokeAsync("Scoresheet") +
+ +
+ +
+ + + +

Preview

+
+
+
+
+
+ + +} diff --git a/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/ScoresheetConfiguration/ScoresheetConfigurationViewComponent.cs b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/ScoresheetConfiguration/ScoresheetConfigurationViewComponent.cs new file mode 100644 index 0000000000..0a81f69924 --- /dev/null +++ b/applications/Unity.GrantManager/modules/Unity.Flex/src/Unity.Flex.Web/Views/Shared/Components/ScoresheetConfiguration/ScoresheetConfigurationViewComponent.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.Bundling; +using Volo.Abp.AspNetCore.Mvc.UI.Widgets; + +namespace Unity.Flex.Web.Views.Shared.Components.ScoresheetConfiguration; + +[Widget( + ScriptTypes = [typeof(ScoresheetConfigurationScriptBundleContributor)], + StyleTypes = [typeof(ScoresheetConfigurationStyleBundleContributor)], + AutoInitialize = true)] +public class ScoresheetConfigurationViewComponent(IConfiguration configuration) : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View(new ScoresheetConfigurationViewModel + { + MaxFileSize = configuration["S3:MaxFileSize"] ?? "" + }); + } +} + +public class ScoresheetConfigurationViewModel +{ + public string MaxFileSize { get; set; } = ""; +} + +public class ScoresheetConfigurationStyleBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/Pages/ScoresheetConfiguration/Index.css"); + } +} + +public class ScoresheetConfigurationScriptBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.AddIfNotContains("/libs/sortablejs/Sortable.js"); + context.Files.AddIfNotContains("/Pages/WorksheetConfiguration/Index.js"); + context.Files.AddIfNotContains("/Pages/ScoresheetConfiguration/Index.js"); + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml index a53c96f106..d2a07c68fc 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.cshtml @@ -1,5 +1,6 @@ @page @using Microsoft.AspNetCore.Mvc.Localization +@using Unity.Flex.Web.Views.Shared.Components.ScoresheetConfiguration @using Unity.Flex.Web.Views.Shared.Components.WorksheetConfiguration @using Unity.GrantManager.Localization @using Unity.GrantManager.Web.Pages.ConfigurationManagement @@ -35,7 +36,6 @@ { } - @if (Model.ShowNotifications) { @@ -216,42 +216,7 @@ @if (Model.ShowScoresheets) {
-
-
-
-
- - - -
-
- @await Component.InvokeAsync("Scoresheet") -
-
- -
-
-
-
- - -

Preview

-
-
-
-
-
-
+ @await Component.InvokeAsync(typeof(ScoresheetConfigurationViewComponent))
} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css index 3e2796dc65..0f7771ad4b 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ConfigurationManagement/Index.css @@ -40,12 +40,19 @@ .config-management { padding: 1rem; - max-height: 80vh; - overflow: auto; + max-height: calc(100vh - 140px); + overflow: hidden; margin-top: 20px; width: 95%; } +/* When scroll-managed components are embedded in the config container, + constrain their height so the container's padding is visible at the bottom */ +.config-management .scoresheet-scrollable-content, +.config-management .worksheet-scrollable-content { + height: calc(100vh - 210px); +} + .unity-app-main-container { margin: auto; display: flex; From 858f233d344501f215bd5357d7a645ecd70318c2 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 13 May 2026 11:36:20 -0700 Subject: [PATCH 09/10] AB#32986 make AI runtime configuration explicit --- .../AI/Operations/AIExecutionModeResolver.cs | 8 +- .../AI/Prompts/Versions/README.md | 5 +- .../AI/Runtime/OpenAIConfigurationResolver.cs | 114 +++++++------- .../AI/Runtime/OpenAIPromptRenderer.cs | 12 +- .../AI/Runtime/OpenAIRuntimeService.cs | 54 ++----- .../AI/Runtime/OpenAITransportService.cs | 6 - .../appsettings.Development.json | 44 +----- .../Unity.GrantManager.Web/appsettings.json | 10 +- .../AIExecutionModeResolverTests.cs | 30 ++++ .../OpenAIConfigurationResolverTests.cs | 149 ++++++++++++++++++ .../AI/Runtime/OpenAIPromptRendererTests.cs | 29 ++++ 11 files changed, 305 insertions(+), 156 deletions(-) create mode 100644 applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIPromptRendererTests.cs diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Operations/AIExecutionModeResolver.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Operations/AIExecutionModeResolver.cs index 90ce666fa8..d7fae9b818 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Operations/AIExecutionModeResolver.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Operations/AIExecutionModeResolver.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using System; using Unity.AI.Prompts; using Volo.Abp.DependencyInjection; @@ -6,9 +7,9 @@ namespace Unity.AI.Operations; /// /// Resolves the configured for an AI operation. -/// Configuration keys (all optional, default = Sequential): +/// Configuration keys: /// Azure:Operations:{operationName}:ExecutionMode - "Sequential" | "Parallel" | "Batch" (case-insensitive) -/// Azure:Operations:Defaults:ExecutionMode - default when operation override is absent +/// Azure:Operations:Defaults:ExecutionMode - required default when operation override is absent /// public class AIExecutionModeResolver(IConfiguration configuration) : ITransientDependency { @@ -25,9 +26,10 @@ public AIExecutionMode ResolveMode(string operationName) return configured?.Trim().ToLowerInvariant() switch { + "sequential" => AIExecutionMode.Sequential, "parallel" => AIExecutionMode.Parallel, "batch" => AIExecutionMode.Batch, - _ => AIExecutionMode.Sequential + _ => throw new InvalidOperationException($"AI execution mode is not configured or is invalid for operation '{operationName}'.") }; } } diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Prompts/Versions/README.md b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Prompts/Versions/README.md index 3cc30965e8..21255c7dff 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Prompts/Versions/README.md +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Prompts/Versions/README.md @@ -40,9 +40,8 @@ Placeholders: Version selection: -- Preferred: `Azure:Operations:Defaults:PromptVersion = v0|v1`, with optional overrides under `Azure:Operations::PromptVersion` -- Legacy fallback: `Azure:OpenAI:PromptVersion = v0|v1` -- Unknown or missing version defaults to `v1`. +- Required: `Azure:Operations:Defaults:PromptVersion = v0|v1`, with optional overrides under `Azure:Operations::PromptVersion`. +- Unknown or missing version values fail at runtime. Template loading is strict: diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs index 25fb917d21..d8d7b2be3f 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs @@ -7,46 +7,40 @@ namespace Unity.AI.Runtime; public class OpenAIConfigurationResolver(IConfiguration configuration) : ITransientDependency { - private const string DefaultMaxTokensParameterName = "max_completion_tokens"; - private const string LegacyMaxTokensParameterName = "max_tokens"; - private const string DefaultProviderName = "OpenAI"; - private readonly IConfiguration _configuration = configuration; public string ResolveProviderName(string? operationName = null) { if (!string.IsNullOrWhiteSpace(operationName)) { - var configuredProvider = _configuration[$"Azure:Operations:{operationName}:Provider"]; - if (!string.IsNullOrWhiteSpace(configuredProvider)) + var operationProvider = Optional($"Azure:Operations:{operationName}:Provider"); + if (operationProvider != null) { - return configuredProvider.Trim(); + return operationProvider; } } - var defaultProvider = _configuration["Azure:Operations:Defaults:Provider"]; - return string.IsNullOrWhiteSpace(defaultProvider) ? DefaultProviderName : defaultProvider.Trim(); + return Required("Azure:Operations:Defaults:Provider"); } public string ResolveApiKey(string? operationName = null) { var providerName = ResolveProviderName(operationName); - return _configuration[$"Azure:{providerName}:ApiKey"] ?? string.Empty; + return Required($"Azure:{providerName}:ApiKey"); } public string ResolveMaxTokensParameterNameForOperation(string? operationName = null) { var providerName = ResolveProviderName(operationName); var profileName = ResolveProfileName(operationName); - var profileParameterName = ResolveProfileSetting(providerName, profileName, "MaxTokensParameter"); - return ResolveMaxTokensParameterName(profileParameterName); + return RequiredProfile(providerName, profileName, "MaxTokensParameter"); } public double? ResolveConfiguredTemperature(string? operationName = null) { var providerName = ResolveProviderName(operationName); var profileName = ResolveProfileName(operationName); - var profileTemperature = ResolveProfileSetting(providerName, profileName, "Temperature"); + var profileTemperature = OptionalProfile(providerName, profileName, "Temperature"); if (profileTemperature != null && double.TryParse(profileTemperature, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedTemperature)) { @@ -56,83 +50,83 @@ public string ResolveMaxTokensParameterNameForOperation(string? operationName = return null; } - public int ResolveCompletionTokens(string operationName, int defaultValue) + public int ResolveCompletionTokens(string operationName) { - var configuredValue = _configuration.GetValue($"Azure:Operations:{operationName}:MaxCompletionTokens"); + var configuredValue = OptionalPositiveInt($"Azure:Operations:{operationName}:MaxCompletionTokens"); if (configuredValue is > 0) { return configuredValue.Value; } - var defaultConfiguredValue = _configuration.GetValue("Azure:Operations:Defaults:MaxCompletionTokens"); - return defaultConfiguredValue is > 0 ? defaultConfiguredValue.Value : defaultValue; + var defaultConfiguredValue = OptionalPositiveInt("Azure:Operations:Defaults:MaxCompletionTokens"); + if (defaultConfiguredValue is > 0) + { + return defaultConfiguredValue.Value; + } + + throw new InvalidOperationException($"AI max completion tokens are not configured for operation '{operationName}'."); + } + + public string ResolvePromptVersion(string operationName) + { + return Optional($"Azure:Operations:{operationName}:PromptVersion") + ?? Required("Azure:Operations:Defaults:PromptVersion"); } public string ResolveApiUrl(string? operationName = null) { var providerName = ResolveProviderName(operationName); + var endpoint = Required($"Azure:{providerName}:Endpoint"); var profileName = ResolveProfileName(operationName); - var profileApiUrl = ResolveProfileSetting(providerName, profileName, "ApiUrl"); - var injectedEndpoint = ResolveInjectedEndpoint(providerName); - var legacyOpenAiApiUrl = _configuration["Azure:OpenAI:ApiUrl"]; - - if (!string.IsNullOrWhiteSpace(injectedEndpoint) && !string.IsNullOrWhiteSpace(profileApiUrl)) - { - return CombineEndpointAndPath(injectedEndpoint, profileApiUrl); - } - - if (!string.IsNullOrWhiteSpace(profileApiUrl)) - { - return profileApiUrl; - } + var profileApiUrl = RequiredProfile(providerName, profileName, "ApiUrl"); + return CombineEndpointAndPath(endpoint, profileApiUrl); + } - if (!string.IsNullOrWhiteSpace(legacyOpenAiApiUrl)) + private string ResolveProfileName(string? operationName) + { + if (!string.IsNullOrWhiteSpace(operationName)) { - return legacyOpenAiApiUrl; + var operationProfile = Optional($"Azure:Operations:{operationName}:Profile"); + if (operationProfile != null) + { + return operationProfile; + } } - throw new InvalidOperationException($"AI API URL is not configured for provider '{providerName}'."); + return Required("Azure:Operations:Defaults:Profile"); } - private static string ResolveMaxTokensParameterName(string? configuredParameterName) + private string RequiredProfile(string providerName, string profileName, string settingName) { - if (string.Equals(configuredParameterName, LegacyMaxTokensParameterName, StringComparison.Ordinal)) - { - return LegacyMaxTokensParameterName; - } - - return DefaultMaxTokensParameterName; + var key = ProfileKey(providerName, profileName, settingName); + return Required(key); } - private string? ResolveInjectedEndpoint(string providerName) + private string? OptionalProfile(string providerName, string profileName, string settingName) { - return _configuration[$"Azure:{providerName}:Endpoint"]; + return Optional(ProfileKey(providerName, profileName, settingName)); } - private string? ResolveProfileName(string? operationName) + private string Required(string key) { - if (!string.IsNullOrWhiteSpace(operationName)) - { - var operationProfile = _configuration[$"Azure:Operations:{operationName}:Profile"]; - if (!string.IsNullOrWhiteSpace(operationProfile)) - { - return operationProfile.Trim(); - } - } + return Optional(key) ?? throw new InvalidOperationException($"{key} is not configured."); + } - var defaultProfile = _configuration["Azure:Operations:Defaults:Profile"]; - return string.IsNullOrWhiteSpace(defaultProfile) ? null : defaultProfile.Trim(); + private string? Optional(string key) + { + var value = _configuration[key]; + return string.IsNullOrWhiteSpace(value) ? null : value.Trim(); } - private string? ResolveProfileSetting(string providerName, string? profileName, string settingName) + private int? OptionalPositiveInt(string key) { - if (string.IsNullOrWhiteSpace(profileName)) - { - return null; - } + var value = _configuration.GetValue(key); + return value is > 0 ? value : null; + } - var profileSetting = _configuration[$"Azure:{providerName}:Profiles:{profileName}:{settingName}"]; - return string.IsNullOrWhiteSpace(profileSetting) ? null : profileSetting; + private static string ProfileKey(string providerName, string profileName, string settingName) + { + return $"Azure:{providerName}:Profiles:{profileName}:{settingName}"; } private static string CombineEndpointAndPath(string endpoint, string profilePath) diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIPromptRenderer.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIPromptRenderer.cs index e011df8819..c90ed92916 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIPromptRenderer.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIPromptRenderer.cs @@ -20,7 +20,7 @@ public class OpenAIPromptRenderer : ITransientDependency private const string ApplicationScoringSystemTemplateName = "application-scoring.system"; private const string ApplicationScoringUserTemplateName = "application-scoring.user"; private static readonly Dictionary PromptProfiles = - new(StringComparer.OrdinalIgnoreCase) + new(StringComparer.Ordinal) { [PromptVersionV0] = PromptVersionV0, [PromptVersionV1] = PromptVersionV1 @@ -184,13 +184,17 @@ public static string BuildAliasedApplicationScoringSection(string? sectionName, public static string ResolvePromptVersion(string? version) { - if (!string.IsNullOrWhiteSpace(version) && - PromptProfiles.TryGetValue(version.Trim(), out var selectedVersion)) + if (string.IsNullOrWhiteSpace(version)) + { + throw new InvalidOperationException("AI prompt version is not configured."); + } + + if (PromptProfiles.TryGetValue(version.Trim(), out var selectedVersion)) { return selectedVersion; } - return PromptVersionV1; + throw new InvalidOperationException($"AI prompt version '{version}' is not supported."); } private static bool TryGetPromptTemplate(string version, string templateName, out string template) diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIRuntimeService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIRuntimeService.cs index ded93f28cd..b61fed77c1 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIRuntimeService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIRuntimeService.cs @@ -26,15 +26,10 @@ public class OpenAIRuntimeService : IAIService, ITransientDependency private const string AttachmentSummaryPromptType = AIPromptTypes.AttachmentSummary; private const string ApplicationScoringPromptType = AIPromptTypes.ApplicationScoring; private const int MaxAiAttempts = 3; - private const int DefaultAttachmentSummaryCompletionTokens = 2000; - private const int DefaultApplicationAnalysisCompletionTokens = 4000; - private const int DefaultApplicationScoringCompletionTokens = 8000; - - private int AttachmentSummaryCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(AttachmentSummaryPromptType, DefaultAttachmentSummaryCompletionTokens); - private int ApplicationAnalysisCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(ApplicationAnalysisPromptType, DefaultApplicationAnalysisCompletionTokens); - private int ApplicationScoringCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(ApplicationScoringPromptType, DefaultApplicationScoringCompletionTokens); - private readonly string MissingApiKeyMessage = "OpenAI API key is not configured"; + private int AttachmentSummaryCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(AttachmentSummaryPromptType); + private int ApplicationAnalysisCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(ApplicationAnalysisPromptType); + private int ApplicationScoringCompletionTokens => _openAIConfigurationResolver.ResolveCompletionTokens(ApplicationScoringPromptType); // Optional local debugging sink for prompt payload logs to a local file. // Not intended for deployed/shared environments. private bool IsPromptFileLoggingEnabled => _configuration.GetValue("Azure:Logging:EnablePromptFileLog") ?? false; @@ -57,19 +52,23 @@ public OpenAIRuntimeService( public Task IsAvailableAsync() { - if (string.IsNullOrEmpty(_openAIConfigurationResolver.ResolveApiKey())) + try { - _logger.LogWarning("Error: {Message}", MissingApiKeyMessage); + _openAIConfigurationResolver.ResolveApiKey(); + return Task.FromResult(true); + } + catch (InvalidOperationException ex) + { + _logger.LogWarning(ex, "AI is unavailable because the OpenAI configuration could not be resolved."); return Task.FromResult(false); } - - return Task.FromResult(true); } public async Task GenerateApplicationAnalysisAsync(ApplicationAnalysisRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); - var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion(request.PromptVersion ?? ResolvePromptVersionSetting(ApplicationAnalysisPromptType)); + var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion( + request.PromptVersion ?? _openAIConfigurationResolver.ResolvePromptVersion(ApplicationAnalysisPromptType)); var data = JsonSerializer.Serialize(request.Data, AIJsonDefaults.Indented); var schema = JsonSerializer.Serialize(request.Schema, AIJsonDefaults.Indented); @@ -115,7 +114,8 @@ public async Task GenerateAttachmentSummaryAsync(Atta ArgumentNullException.ThrowIfNull(request); var fileName = request.FileName ?? string.Empty; var contentType = request.ContentType ?? "application/octet-stream"; - var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion(request.PromptVersion ?? ResolvePromptVersionSetting(AttachmentSummaryPromptType)); + var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion( + request.PromptVersion ?? _openAIConfigurationResolver.ResolvePromptVersion(AttachmentSummaryPromptType)); try { @@ -186,19 +186,14 @@ public async Task GenerateAttachmentSummaryAsync(Atta public async Task GenerateApplicationScoringAsync(ApplicationScoringRequest request, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(request); - var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion(request.PromptVersion ?? ResolvePromptVersionSetting(ApplicationScoringPromptType)); + var promptVersion = OpenAIPromptRenderer.ResolvePromptVersion( + request.PromptVersion ?? _openAIConfigurationResolver.ResolvePromptVersion(ApplicationScoringPromptType)); var dataJson = JsonSerializer.Serialize(request.Data, AIJsonDefaults.Indented); var sectionJson = JsonSerializer.Serialize(request.SectionSchema, AIJsonDefaults.Indented); var attachmentSummaries = request.Attachments .Select(a => $"{a.Name}: {a.Summary}") .ToList(); - if (string.IsNullOrEmpty(_openAIConfigurationResolver.ResolveApiKey(ApplicationScoringPromptType))) - { - _logger.LogWarning("{Message}", MissingApiKeyMessage); - return new ApplicationScoringResponse(); - } - try { var attachments = attachmentSummaries.Count > 0 @@ -320,23 +315,6 @@ private async Task GenerateWithRetryAsync( : null; } - private string? ResolvePromptVersionSetting(string operationName) - { - var operationPromptVersion = _configuration[$"Azure:Operations:{operationName}:PromptVersion"]; - if (!string.IsNullOrWhiteSpace(operationPromptVersion)) - { - return operationPromptVersion; - } - - var defaultPromptVersion = _configuration["Azure:Operations:Defaults:PromptVersion"]; - if (!string.IsNullOrWhiteSpace(defaultPromptVersion)) - { - return defaultPromptVersion; - } - - return _configuration["Azure:OpenAI:PromptVersion"]; - } - private async Task LogPromptInputAsync(string promptType, string promptVersion, string? systemPrompt, string userPrompt, CancellationToken cancellationToken = default) { var formattedInput = FormatPromptInputForLog(systemPrompt, userPrompt); diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs index f5bc0e2192..2d39208c83 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs @@ -38,12 +38,6 @@ public async Task GenerateSummaryAsync( } var apiKey = _configurationResolver.ResolveApiKey(operationName); - if (string.IsNullOrEmpty(apiKey)) - { - _logger.LogWarning("Error: OpenAI API key is not configured"); - return AIOperationResult.PermanentFailure(new AIProviderResult("OpenAI API key is not configured")); - } - try { var resolvedSystemPrompt = string.IsNullOrWhiteSpace(systemPrompt) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json index da702b7109..7529e8a24c 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json @@ -136,40 +136,10 @@ "JWTSecret": "" }, - "Azure": { - "Operations": { - "Defaults": { - "Provider": "OpenAI", - "Profile": "Gpt4oMini", - "PromptVersion": "v1" - }, - "AttachmentSummary": { - "MaxCompletionTokens": 2000 - }, - "ApplicationAnalysis": { - "MaxCompletionTokens": 4000 - }, - "ApplicationScoring": { - "MaxCompletionTokens": 8000 - } - }, - "OpenAI": { - "ApiKey": "", - "Endpoint": "", - "Profiles": { - "Gpt4oMini": { - "ApiUrl": "/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01", - "MaxTokensParameter": "max_tokens", - "Temperature": 0.3 - }, - "Gpt5Mini": { - "ApiUrl": "/openai/deployments/gpt-5-mini/chat/completions?api-version=2024-10-01-preview", - "MaxTokensParameter": "max_completion_tokens" - } - } - }, - "Logging": { - "EnablePromptFileLog": false - } - } -} + "Azure": { + "OpenAI": { + "ApiKey": "", + "Endpoint": "" + } + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json index af1e0fe43f..a629ab19fb 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json @@ -172,11 +172,11 @@ "ApiKey": "", "Endpoint": "", "Profiles": { - "Gpt4oMini": { - "ApiUrl": "/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01", - "MaxTokensParameter": "max_tokens", - "Temperature": 0.3 - }, + "Gpt4oMini": { + "ApiUrl": "/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01", + "MaxTokensParameter": "max_tokens", + "Temperature": 0.3 + }, "Gpt5Mini": { "ApiUrl": "/openai/deployments/gpt-5-mini/chat/completions?api-version=2024-10-01-preview", "MaxTokensParameter": "max_completion_tokens" diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Operations/AIExecutionModeResolverTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Operations/AIExecutionModeResolverTests.cs index 116a8b29ec..2762b404e5 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Operations/AIExecutionModeResolverTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Operations/AIExecutionModeResolverTests.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using Shouldly; +using System; using System.Collections.Generic; using Unity.AI.Operations; using Unity.AI.Prompts; @@ -25,4 +26,33 @@ public void ResolveMode_Uses_Operation_Override_Before_Default() resolver.ResolveMode(AIPromptTypes.ApplicationScoring).ShouldBe(AIExecutionMode.Batch); resolver.ResolveMode(AIPromptTypes.AttachmentSummary).ShouldBe(AIExecutionMode.Parallel); } + + [Fact] + public void ResolveMode_Should_Throw_When_Default_Is_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary()) + .Build(); + + var resolver = new AIExecutionModeResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveMode(AIPromptTypes.AttachmentSummary)); + ex.Message.ShouldContain(AIPromptTypes.AttachmentSummary); + } + + [Fact] + public void ResolveMode_Should_Throw_When_Configured_Value_Is_Invalid() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:ExecutionMode"] = "Fast" + }) + .Build(); + + var resolver = new AIExecutionModeResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveMode(AIPromptTypes.AttachmentSummary)); + ex.Message.ShouldContain(AIPromptTypes.AttachmentSummary); + } } diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs index aa95ac7b8a..ca20cbfad3 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using Shouldly; +using System; using System.Collections.Generic; using Unity.AI.Runtime; using Xunit; @@ -26,4 +27,152 @@ public void ResolveApiUrl_Should_CombineEndpointWithLeadingSlashProfilePath() resolver.ResolveApiUrl().ShouldBe( "https://d837ad-test-recap-webapp.azurewebsites.net/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01"); } + + [Fact] + public void ResolveProviderName_Should_Throw_When_DefaultProvider_Is_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary()) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveProviderName()); + ex.Message.ShouldContain("Azure:Operations:Defaults:Provider"); + } + + [Fact] + public void ResolveApiUrl_Should_Throw_When_Endpoint_Is_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt4oMini", + ["Azure:OpenAI:Profiles:Gpt4oMini:ApiUrl"] = "/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01" + }) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveApiUrl()); + ex.Message.ShouldContain("Azure:OpenAI:Endpoint"); + } + + [Fact] + public void ResolveMaxTokensParameterNameForOperation_Should_Return_Configured_Profile_Parameter() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt4oMini", + ["Azure:OpenAI:Profiles:Gpt4oMini:MaxTokensParameter"] = "max_tokens" + }) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveMaxTokensParameterNameForOperation().ShouldBe("max_tokens"); + } + + [Fact] + public void ResolveCompletionTokens_Should_Throw_When_Operation_And_Default_Are_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary()) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveCompletionTokens("ApplicationAnalysis")); + ex.Message.ShouldContain("ApplicationAnalysis"); + } + + [Fact] + public void ResolvePromptVersion_Should_Use_Operation_Override_Before_Default() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:PromptVersion"] = "v0", + ["Azure:Operations:ApplicationAnalysis:PromptVersion"] = "v1" + }) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolvePromptVersion("ApplicationAnalysis").ShouldBe("v1"); + } + + [Fact] + public void ResolvePromptVersion_Should_Throw_When_Default_Is_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary()) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolvePromptVersion("ApplicationAnalysis")); + ex.Message.ShouldContain("Azure:Operations:Defaults:PromptVersion"); + } + + [Fact] + public void ResolveApiKey_Should_Throw_When_Configured_Key_Is_Missing() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI" + }) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + var ex = Should.Throw(() => resolver.ResolveApiKey()); + ex.Message.ShouldContain("Azure:OpenAI:ApiKey"); + } + + [Fact] + public void ResolveConfiguredTemperature_Should_Return_Profile_Temperature() + { + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt4oMini", + ["Azure:OpenAI:Profiles:Gpt4oMini:Temperature"] = "0.3" + }) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveConfiguredTemperature().ShouldBe(0.3); + } + + [Theory] + [InlineData(null)] + [InlineData("not-a-number")] + public void ResolveConfiguredTemperature_Should_Return_Null_When_Profile_Temperature_Is_Missing_Or_Invalid(string? temperature) + { + var values = new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt4oMini" + }; + + if (temperature != null) + { + values["Azure:OpenAI:Profiles:Gpt4oMini:Temperature"] = temperature; + } + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build(); + + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveConfiguredTemperature().ShouldBeNull(); + } } diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIPromptRendererTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIPromptRendererTests.cs new file mode 100644 index 0000000000..7d7258808b --- /dev/null +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIPromptRendererTests.cs @@ -0,0 +1,29 @@ +using Shouldly; +using System; +using Unity.AI.Runtime; +using Xunit; + +namespace Unity.GrantManager.AI.Runtime; + +public class OpenAIPromptRendererTests +{ + [Theory] + [InlineData("v0")] + [InlineData("v1")] + [InlineData(" v1 ")] + public void ResolvePromptVersion_Should_Return_Supported_Version(string version) + { + OpenAIPromptRenderer.ResolvePromptVersion(version) + .ShouldBe(version.Trim().ToLowerInvariant()); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("V1")] + [InlineData("vNext")] + public void ResolvePromptVersion_Should_Throw_When_Version_Is_Missing_Or_Unsupported(string? version) + { + Should.Throw(() => OpenAIPromptRenderer.ResolvePromptVersion(version)); + } +} From 90a401cf87ae16ecaa538f1c8ebcccaf86adf915 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 8 May 2026 10:24:13 -0700 Subject: [PATCH 10/10] AB#33006 enable GPT-5 mini profile compatibility --- .../AI/Runtime/OpenAIConfigurationResolver.cs | 33 +++++ .../AI/Runtime/OpenAITransportService.cs | 20 ++- .../Unity.GrantManager.Web/appsettings.json | 20 +-- .../OpenAIConfigurationResolverTests.cs | 50 +++++++ .../AI/Runtime/OpenAITransportServiceTests.cs | 131 ++++++++++++++++++ 5 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAITransportServiceTests.cs diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs index d8d7b2be3f..fc12e4cd99 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAIConfigurationResolver.cs @@ -7,6 +7,9 @@ namespace Unity.AI.Runtime; public class OpenAIConfigurationResolver(IConfiguration configuration) : ITransientDependency { + private static readonly string[] SupportedReasoningEfforts = ["minimal", "low", "medium", "high"]; + private static readonly string[] SupportedVerbosityValues = ["low", "medium", "high"]; + private readonly IConfiguration _configuration = configuration; public string ResolveProviderName(string? operationName = null) @@ -50,6 +53,16 @@ public string ResolveMaxTokensParameterNameForOperation(string? operationName = return null; } + public string? ResolveConfiguredReasoningEffort(string? operationName = null) + { + return ResolveOptionalProfileValue(operationName, "ReasoningEffort", SupportedReasoningEfforts); + } + + public string? ResolveConfiguredVerbosity(string? operationName = null) + { + return ResolveOptionalProfileValue(operationName, "Verbosity", SupportedVerbosityValues); + } + public int ResolveCompletionTokens(string operationName) { var configuredValue = OptionalPositiveInt($"Azure:Operations:{operationName}:MaxCompletionTokens"); @@ -129,6 +142,26 @@ private static string ProfileKey(string providerName, string profileName, string return $"Azure:{providerName}:Profiles:{profileName}:{settingName}"; } + private string? ResolveOptionalProfileValue(string? operationName, string settingName, string[] supportedValues) + { + var providerName = ResolveProviderName(operationName); + var profileName = ResolveProfileName(operationName); + var configuredValue = OptionalProfile(providerName, profileName, settingName); + if (configuredValue == null) + { + return null; + } + + var trimmedValue = configuredValue.Trim(); + if (Array.Exists(supportedValues, supportedValue => string.Equals(supportedValue, trimmedValue, StringComparison.Ordinal))) + { + return trimmedValue; + } + + throw new InvalidOperationException( + $"AI {settingName} value '{configuredValue}' is not supported. Use one of: {string.Join(", ", supportedValues)}."); + } + private static string CombineEndpointAndPath(string endpoint, string profilePath) { const char UrlPathSeparator = '/'; diff --git a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs index 2d39208c83..b3cafcae36 100644 --- a/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs +++ b/applications/Unity.GrantManager/modules/Unity.AI/src/Unity.AI.Application/AI/Runtime/OpenAITransportService.cs @@ -54,12 +54,25 @@ public async Task GenerateSummaryAsync( [_configurationResolver.ResolveMaxTokensParameterNameForOperation(operationName)] = maxTokens }; - var resolvedTemperature = temperature ?? _configurationResolver.ResolveConfiguredTemperature(operationName); + var profileTemperature = _configurationResolver.ResolveConfiguredTemperature(operationName); + var resolvedTemperature = profileTemperature.HasValue ? temperature ?? profileTemperature : null; if (resolvedTemperature.HasValue) { requestPayload["temperature"] = resolvedTemperature.Value; } + var reasoningEffort = _configurationResolver.ResolveConfiguredReasoningEffort(operationName); + if (!string.IsNullOrWhiteSpace(reasoningEffort)) + { + requestPayload["reasoning_effort"] = reasoningEffort; + } + + var verbosity = _configurationResolver.ResolveConfiguredVerbosity(operationName); + if (!string.IsNullOrWhiteSpace(verbosity)) + { + requestPayload["verbosity"] = verbosity; + } + var json = JsonSerializer.Serialize(requestPayload); var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); @@ -118,6 +131,11 @@ public async Task GenerateSummaryAsync( { throw; } + catch (InvalidOperationException ex) + { + _logger.LogError(ex, "AI request configuration is invalid."); + return AIOperationResult.PermanentFailure(new AIProviderResult(ex.Message)); + } catch (Exception ex) { _logger.LogError(ex, "Error generating AI summary"); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json index a629ab19fb..f6d5bfbff4 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.json @@ -154,7 +154,7 @@ "Operations": { "Defaults": { "Provider": "OpenAI", - "Profile": "Gpt4oMini", + "Profile": "Gpt5Mini", "PromptVersion": "v1", "ExecutionMode": "Sequential" }, @@ -170,19 +170,21 @@ }, "OpenAI": { "ApiKey": "", - "Endpoint": "", - "Profiles": { + "Endpoint": "", + "Profiles": { "Gpt4oMini": { "ApiUrl": "/openai/deployments/gpt-4o-mini/chat/completions?api-version=2024-02-01", "MaxTokensParameter": "max_tokens", "Temperature": 0.3 }, - "Gpt5Mini": { - "ApiUrl": "/openai/deployments/gpt-5-mini/chat/completions?api-version=2024-10-01-preview", - "MaxTokensParameter": "max_completion_tokens" - } - } - }, + "Gpt5Mini": { + "ApiUrl": "/openai/deployments/gpt-5-mini/chat/completions?api-version=2024-10-01-preview", + "MaxTokensParameter": "max_completion_tokens", + "ReasoningEffort": "minimal", + "Verbosity": "low" + } + } + }, "Logging": { "EnablePromptFileLog": false } diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs index ca20cbfad3..98b8aa1fb9 100644 --- a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAIConfigurationResolverTests.cs @@ -175,4 +175,54 @@ public void ResolveConfiguredTemperature_Should_Return_Null_When_Profile_Tempera resolver.ResolveConfiguredTemperature().ShouldBeNull(); } + + [Fact] + public void ResolveConfiguredReasoningEffort_Should_Return_ProfileValue() + { + var configuration = BuildProfileConfiguration("ReasoningEffort", "minimal"); + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveConfiguredReasoningEffort().ShouldBe("minimal"); + } + + [Fact] + public void ResolveConfiguredVerbosity_Should_Return_ProfileValue() + { + var configuration = BuildProfileConfiguration("Verbosity", "low"); + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveConfiguredVerbosity().ShouldBe("low"); + } + + [Fact] + public void ResolveConfiguredReasoningEffort_Should_ReturnNull_WhenProfileSettingMissing() + { + var configuration = BuildProfileConfiguration("Temperature", "0.3"); + var resolver = new OpenAIConfigurationResolver(configuration); + + resolver.ResolveConfiguredReasoningEffort().ShouldBeNull(); + } + + [Fact] + public void ResolveConfiguredVerbosity_Should_RejectUnsupportedValue() + { + var configuration = BuildProfileConfiguration("Verbosity", "verbose"); + var resolver = new OpenAIConfigurationResolver(configuration); + + var exception = Should.Throw(() => resolver.ResolveConfiguredVerbosity()); + exception.Message.ShouldContain("Verbosity"); + exception.Message.ShouldContain("low, medium, high"); + } + + private static IConfiguration BuildProfileConfiguration(string settingName, string settingValue) + { + return new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt5Mini", + [$"Azure:OpenAI:Profiles:Gpt5Mini:{settingName}"] = settingValue + }) + .Build(); + } } diff --git a/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAITransportServiceTests.cs b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAITransportServiceTests.cs new file mode 100644 index 0000000000..8a0c272301 --- /dev/null +++ b/applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/AI/Runtime/OpenAITransportServiceTests.cs @@ -0,0 +1,131 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging.Abstractions; +using Shouldly; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Unity.AI.Runtime; +using Xunit; + +namespace Unity.GrantManager.AI.Runtime; + +public class OpenAITransportServiceTests +{ + [Fact] + public async Task GenerateSummaryAsync_Should_Include_ProfileReasoningEffortAndVerbosity() + { + var handler = new CaptureRequestHandler(); + var resolver = CreateResolver(new Dictionary + { + ["Azure:OpenAI:Profiles:Gpt5Mini:ReasoningEffort"] = "minimal", + ["Azure:OpenAI:Profiles:Gpt5Mini:Verbosity"] = "low" + }); + + var service = new OpenAITransportService( + new HttpClient(handler), + resolver, + NullLogger.Instance); + + await service.GenerateSummaryAsync("content", "system", 100); + + using var payload = JsonDocument.Parse(handler.RequestContent); + payload.RootElement.GetProperty("reasoning_effort").GetString().ShouldBe("minimal"); + payload.RootElement.GetProperty("verbosity").GetString().ShouldBe("low"); + payload.RootElement.TryGetProperty("temperature", out _).ShouldBeFalse(); + } + + [Fact] + public async Task GenerateSummaryAsync_Should_Omit_RequestTemperature_When_ProfileDoesNotSupportTemperature() + { + var handler = new CaptureRequestHandler(); + var service = new OpenAITransportService( + new HttpClient(handler), + CreateResolver(), + NullLogger.Instance); + + await service.GenerateSummaryAsync("content", "system", 100, temperature: 0.2); + + using var payload = JsonDocument.Parse(handler.RequestContent); + payload.RootElement.TryGetProperty("temperature", out _).ShouldBeFalse(); + } + + [Fact] + public async Task GenerateSummaryAsync_Should_ReturnPermanentFailure_When_ProfileOptionIsInvalid() + { + var handler = new CaptureRequestHandler(); + var service = new OpenAITransportService( + new HttpClient(handler), + CreateResolver(new Dictionary + { + ["Azure:OpenAI:Profiles:Gpt5Mini:Verbosity"] = "verbose" + }), + NullLogger.Instance); + + var result = await service.GenerateSummaryAsync("content", "system", 100); + + result.Outcome.ShouldBe(AIOperationOutcome.PermanentFailure); + handler.SendCount.ShouldBe(0); + } + + private static OpenAIConfigurationResolver CreateResolver(Dictionary? overrides = null) + { + var values = new Dictionary + { + ["Azure:Operations:Defaults:Provider"] = "OpenAI", + ["Azure:Operations:Defaults:Profile"] = "Gpt5Mini", + ["Azure:OpenAI:ApiKey"] = "test-key", + ["Azure:OpenAI:Endpoint"] = "https://example.test", + ["Azure:OpenAI:Profiles:Gpt5Mini:ApiUrl"] = "/openai/deployments/gpt-5-mini/chat/completions", + ["Azure:OpenAI:Profiles:Gpt5Mini:MaxTokensParameter"] = "max_completion_tokens" + }; + + if (overrides != null) + { + foreach (var item in overrides) + { + values[item.Key] = item.Value; + } + } + + return new OpenAIConfigurationResolver(new ConfigurationBuilder() + .AddInMemoryCollection(values) + .Build()); + } + + private sealed class CaptureRequestHandler : HttpMessageHandler + { + public string RequestContent { get; private set; } = string.Empty; + + public int SendCount { get; private set; } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + SendCount++; + RequestContent = request.Content == null + ? string.Empty + : await request.Content.ReadAsStringAsync(cancellationToken); + + const string response = """ + { + "choices": [ + { + "message": { + "content": "generated" + }, + "finish_reason": "stop" + } + ] + } + """; + + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(response, Encoding.UTF8, "application/json") + }; + } + } +}