From a960047e47f09531979c47aafef63b2f3c4321a6 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 27 Feb 2026 10:55:54 -0800 Subject: [PATCH 01/57] AB#32008 Add Office document text extraction support (Word/Excel) # Conflicts: # applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs # applications/Unity.GrantManager/src/Unity.GrantManager.Application/Unity.GrantManager.Application.csproj --- .../AI/TextExtractionService.cs | 167 ++++++++++++++++-- .../Unity.GrantManager.Application.csproj | 1 + 2 files changed, 152 insertions(+), 16 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index 3c2b3f2b3..8e7f3d41b 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -1,6 +1,10 @@ using Microsoft.Extensions.Logging; +using NPOI.SS.UserModel; +using NPOI.XWPF.UserModel; using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -12,6 +16,12 @@ namespace Unity.GrantManager.AI public class TextExtractionService : ITextExtractionService, ITransientDependency { private const int MaxExtractedTextLength = 50000; + private const int MaxExcelSheets = 10; + private const int MaxExcelRowsPerSheet = 2000; + private const int MaxExcelCellsPerRow = 50; + private const int MaxDocxParagraphs = 2000; + private const int MaxDocxTableRows = 2000; + private const int MaxDocxTableCellsPerRow = 50; private readonly ILogger _logger; public TextExtractionService(ILogger logger) @@ -29,13 +39,11 @@ public async Task ExtractTextAsync(string fileName, byte[] fileContent, try { - // Normalize content type var normalizedContentType = contentType?.ToLowerInvariant() ?? string.Empty; var extension = Path.GetExtension(fileName)?.ToLowerInvariant() ?? string.Empty; string rawText; - // Handle text-based files if (normalizedContentType.Contains("text/") || extension == ".txt" || extension == ".csv" || @@ -46,37 +54,37 @@ public async Task ExtractTextAsync(string fileName, byte[] fileContent, return NormalizeAndLimitText(rawText, fileName); } - // Handle PDF files if (normalizedContentType.Contains("pdf") || extension == ".pdf") { - rawText = await Task.FromResult(ExtractTextFromPdfFile(fileName, fileContent)); + rawText = ExtractTextFromPdfFile(fileName, fileContent); return NormalizeAndLimitText(rawText, fileName); } - // Handle Word documents if (normalizedContentType.Contains("word") || normalizedContentType.Contains("msword") || normalizedContentType.Contains("officedocument.wordprocessingml") || extension == ".doc" || extension == ".docx") { - // For now, return empty string - can be enhanced with Word parsing library - _logger.LogDebug("Word document text extraction not yet implemented for {FileName}", fileName); + if (extension == ".docx" || normalizedContentType.Contains("officedocument.wordprocessingml")) + { + rawText = ExtractTextFromWordDocx(fileContent); + return NormalizeAndLimitText(rawText, fileName); + } + + _logger.LogDebug("Legacy .doc extraction is not supported for {FileName}", fileName); return string.Empty; } - // Handle Excel files if (normalizedContentType.Contains("excel") || normalizedContentType.Contains("spreadsheet") || extension == ".xls" || extension == ".xlsx") { - // For now, return empty string - can be enhanced with Excel parsing library - _logger.LogDebug("Excel text extraction not yet implemented for {FileName}", fileName); - return string.Empty; + rawText = ExtractTextFromExcelFile(fileName, fileContent); + return NormalizeAndLimitText(rawText, fileName); } - // For other file types, return empty string _logger.LogDebug("No text extraction available for content type {ContentType} with extension {Extension}", contentType, extension); return string.Empty; @@ -92,17 +100,13 @@ private async Task ExtractTextFromTextFileAsync(byte[] fileContent) { try { - // Try UTF-8 first var text = Encoding.UTF8.GetString(fileContent); - // Check if the decoded text contains replacement characters (indicates encoding issue) if (text.Contains('\uFFFD')) { - // Try other encodings text = Encoding.ASCII.GetString(fileContent); } - // Limit the extracted text to a reasonable size. if (text.Length > MaxExtractedTextLength) { text = text.Substring(0, MaxExtractedTextLength); @@ -154,6 +158,137 @@ private string ExtractTextFromPdfFile(string fileName, byte[] fileContent) } } + private string ExtractTextFromWordDocx(byte[] fileContent) + { + try + { + using var stream = new MemoryStream(fileContent, writable: false); + using var document = new XWPFDocument(stream); + var parts = new List(); + + foreach (var paragraph in document.Paragraphs.Take(MaxDocxParagraphs)) + { + if (!string.IsNullOrWhiteSpace(paragraph.ParagraphText)) + { + parts.Add(paragraph.ParagraphText); + } + } + + foreach (var table in document.Tables) + { + foreach (var row in table.Rows.Take(MaxDocxTableRows)) + { + foreach (var cell in row.GetTableCells().Take(MaxDocxTableCellsPerRow)) + { + var text = cell.GetText(); + if (!string.IsNullOrWhiteSpace(text)) + { + parts.Add(text); + } + } + } + } + + var combined = string.Join(Environment.NewLine, parts); + if (combined.Length > MaxExtractedTextLength) + { + combined = combined.Substring(0, MaxExtractedTextLength); + } + + return combined; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Word (.docx) text extraction failed"); + return string.Empty; + } + } + + private string ExtractTextFromExcelFile(string fileName, byte[] fileContent) + { + try + { + using var stream = new MemoryStream(fileContent, writable: false); + using var workbook = WorkbookFactory.Create(stream); + var rows = new List(); + var totalLength = 0; + var sheetCount = Math.Min(workbook.NumberOfSheets, MaxExcelSheets); + + for (var sheetIndex = 0; sheetIndex < sheetCount; sheetIndex++) + { + var sheet = workbook.GetSheetAt(sheetIndex); + if (sheet == null) + { + continue; + } + + var processedRows = 0; + foreach (IRow row in sheet) + { + if (processedRows >= MaxExcelRowsPerSheet || totalLength >= MaxExtractedTextLength) + { + break; + } + + var cellTexts = row.Cells + .Take(MaxExcelCellsPerRow) + .Select(GetCellText) + .Where(value => !string.IsNullOrWhiteSpace(value)) + .ToList(); + + processedRows++; + + if (cellTexts.Count == 0) + { + continue; + } + + var rowText = string.Join(" | ", cellTexts); + rows.Add(rowText); + totalLength += rowText.Length; + } + + if (totalLength >= MaxExtractedTextLength) + { + break; + } + } + + var combined = string.Join(Environment.NewLine, rows); + if (combined.Length > MaxExtractedTextLength) + { + combined = combined.Substring(0, MaxExtractedTextLength); + } + + return combined; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Excel text extraction failed for {FileName}", fileName); + return string.Empty; + } + } + + private static string GetCellText(ICell cell) + { + if (cell == null) + { + return string.Empty; + } + + return (cell.CellType switch + { + CellType.String => cell.StringCellValue ?? string.Empty, + CellType.Numeric => DateUtil.IsCellDateFormatted(cell) + ? cell.DateCellValue.ToString() + : cell.NumericCellValue.ToString(), + CellType.Boolean => cell.BooleanCellValue ? "true" : "false", + CellType.Formula => cell.ToString(), + CellType.Blank => string.Empty, + _ => cell.ToString() ?? string.Empty + }) ?? string.Empty; + } + private string NormalizeAndLimitText(string text, string fileName) { var normalized = NormalizeExtractedText(text); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Unity.GrantManager.Application.csproj b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Unity.GrantManager.Application.csproj index 8ec3e53bc..ff57bfd94 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Unity.GrantManager.Application.csproj +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Unity.GrantManager.Application.csproj @@ -33,6 +33,7 @@ + From 7ed4d3f3a94e2700c9df5892b134203912b8b4cd Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 27 Feb 2026 12:01:21 -0800 Subject: [PATCH 02/57] AB#32008 Resolve ICell specificity error --- .../Unity.GrantManager.Application/AI/TextExtractionService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index 8e7f3d41b..8de6b180e 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -269,7 +269,7 @@ private string ExtractTextFromExcelFile(string fileName, byte[] fileContent) } } - private static string GetCellText(ICell cell) + private static string GetCellText(NPOI.SS.UserModel.ICell cell) { if (cell == null) { From 39899ccaa044ae69b37683760689b460e88423ad Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Fri, 27 Feb 2026 13:02:26 -0800 Subject: [PATCH 03/57] Added new placeholders on appsettings. Added ESlint. --- applications/Unity.GrantManager/.env.example | 2 +- .../appsettings.Development.json | 58 ++- .../src/Unity.GrantManager.Web/package.json | 11 +- .../src/Unity.GrantManager.Web/yarn.lock | 488 +++++++++++++++++- 4 files changed, 528 insertions(+), 31 deletions(-) diff --git a/applications/Unity.GrantManager/.env.example b/applications/Unity.GrantManager/.env.example index 3a39780fa..c2094e4c0 100644 --- a/applications/Unity.GrantManager/.env.example +++ b/applications/Unity.GrantManager/.env.example @@ -41,7 +41,7 @@ AuthServer__Realm="standard" #"unity-local" AuthServer__RequireHttpsMetadata="false" AuthServer__Audience="unity-4899" #"unity-web" AuthServer__ClientId="unity-4899" #"unity-web" -AuthServer__ClientSecret="="********"" +AuthServer__ClientSecret="********" AuthServer__IsBehindTlsTerminationProxy="false" AuthServer__SpecifyOidcParameters="true" AuthServer__OidcSignin="http://localhost:44342/signin-oidc" 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 27323616e..e41b752c5 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/appsettings.Development.json @@ -2,6 +2,18 @@ "App": { "SelfUrl": "https://localhost:44342" }, + "AuthServer": { + "ClientSecret": "" + }, + "S3": { + "AccessKeyId": "", + "Bucket": "", + "Endpoint": "", + "SecretAccessKey": "" + }, + "CssApi": { + "ClientSecret": "" + }, "ConnectionStrings": { "Default": "Host=localhost;port=5432;Database=UnityGrantManager;Username=postgres;", "Tenant": "Host=localhost;port=5432;Database=UnityGrantTenant;Username=postgres;" @@ -14,8 +26,29 @@ "Quartz": { "UseCluster": false, "IsAutoRegisterEnabled": false + }, + "IntakeResync": { + "Expression": "0 0 12 1/1 * ? *", + "NumDaysToCheck": "-2" + }, + "EmailResend": { + "RetryAttemptsMaximum": 2 + }, + "CasPaymentsReconciliation": { + "ProducerExpression": "0 0 22 1/1 * ? *" + }, + "CasFinancialNotificationSummary": { + "ProducerExpression": "0 0 23 1/1 * ? *" } }, + "RabbitMQ": { + "IsEnabled": false, + "HostName": "127.0.0.1", + "Port": 5672, + "UserName": "guest", + "Password": "guest", + "VirtualHost": "/" + }, "Intake": { "BaseUri": "https://chefs-test.apps.silver.devops.gov.bc.ca/app/api/v1", "FormId": "", @@ -28,16 +61,17 @@ "AllowUnregisteredVersions": true }, "Payments": { - "CasBaseUrl": "", - "CasClientId": "", - "CasClientSecret": "" + "CasBaseUrl": "", + "CasClientId": "", + "CasClientSecret": "" }, "Notifications": { "TeamsNotificationsWebhook": "", "ChesUrl": "https://ches-dev.api.gov.bc.ca/api/v1", "ChesTokenUrl": "https://dev.loginproxy.gov.bc.ca/auth/realms/comsvcauth/protocol/openid-connect/token", "ChesClientId": "", - "ChesClientSecret": "" + "ChesClientSecret": "", + "ChesBaseUri": "https://ches-dev.api.gov.bc.ca/api/v1" }, "DataProtection": { "IsEnabled": false @@ -97,5 +131,19 @@ }, "B2BAuth": { "ApiKey": "__SET_VIA_USER_SECRETS__" - } + }, + "ReportingAI": { + "JWTSecret": "" + }, + "Azure": { + "OpenAI": { + "ApiKey": "", + "ApiUrl": "", + "Model": "" + }, + "AgenticAPI": { + "Url": "http://localhost:5000/v1/completions" + } + } + } \ No newline at end of file diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json index 63cd5b81d..ed970e1b7 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json @@ -16,8 +16,10 @@ "datatables.net-select-bs5": "~3.1.0", "datatables.net-staterestore": "~1.4.2", "datatables.net-staterestore-dt": "~1.4.2", + "dompurify": "^3.3.1", "echarts": "~6.0.0", "formiojs": "4.17.4", + "handlebars": "~4.7.8", "html2canvas": "~1.4.1", "jquery-maskmoney": "~3.0.2", "jspdf": "~4.0.0", @@ -25,12 +27,11 @@ "popper.js": "~1.16.1", "pubsub-js": "~1.9.5", "sortablejs": "~1.15.6", - "tributejs": "~5.1.3", "tinymce": "~8.3.2", - "handlebars": "~4.7.8", - "dompurify": "^3.3.1" + "tributejs": "~5.1.3" }, "devDependencies": { - "@types/jquery": "~3.5.33" + "@types/jquery": "~3.5.33", + "eslint": "^10.0.2" } -} \ No newline at end of file +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock index aa748a116..ead5a4d63 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock @@ -198,6 +198,54 @@ resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz" integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== +"@eslint-community/eslint-utils@^4.8.0": + version "4.9.1" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.2": + version "4.12.2" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== + +"@eslint/config-array@^0.23.2": + version "0.23.2" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.2.tgz" + integrity sha512-YF+fE6LV4v5MGWRGj7G404/OZzGNepVF8fxk7jqmqo3lrza7a0uUcDnROGRBG1WFC1omYUS/Wp1f42i0M+3Q3A== + dependencies: + "@eslint/object-schema" "^3.0.2" + debug "^4.3.1" + minimatch "^10.2.1" + +"@eslint/config-helpers@^0.5.2": + version "0.5.2" + resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.2.tgz" + integrity sha512-a5MxrdDXEvqnIq+LisyCX6tQMPF/dSJpCfBgBauY+pNZ28yCtSsTvyTYrMhaI+LK26bVyCJfJkT0u8KIj2i1dQ== + dependencies: + "@eslint/core" "^1.1.0" + +"@eslint/core@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@eslint/core/-/core-1.1.0.tgz" + integrity sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/object-schema@^3.0.2": + version "3.0.2" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.2.tgz" + integrity sha512-HOy56KJt48Bx8KmJ+XGQNSUMT/6dZee/M54XyUyuvTvPXJmsERRvBchsUVx1UMe1WwIH49XLAczNC7V2INsuUw== + +"@eslint/plugin-kit@^0.6.0": + version "0.6.0" + resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.0.tgz" + integrity sha512-bIZEUzOI1jkhviX2cp5vNyXQc6olzb2ohewQubuYlMXZ2Q/XjBO0x0XhGPvc9fjSIiUN0vw+0hq53BJ4eQSJKQ== + dependencies: + "@eslint/core" "^1.1.0" + levn "^0.4.1" + "@formio/bootstrap3@2.12.4-rc.1": version "2.12.4-rc.1" resolved "https://registry.npmjs.org/@formio/bootstrap3/-/bootstrap3-2.12.4-rc.1.tgz" @@ -234,7 +282,30 @@ resolved "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz" integrity sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA== -"@popperjs/core@^2.9.0": +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.7" + resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.4.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== + +"@popperjs/core@^2.11.8", "@popperjs/core@^2.9.0": version "2.11.8" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== @@ -244,6 +315,16 @@ resolved "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz" integrity sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw== +"@types/esrecurse@^4.3.1": + version "4.3.1" + resolved "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz" + integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== + +"@types/estree@^1.0.6", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/jquery@~3.5.33": version "3.5.33" resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz" @@ -251,6 +332,11 @@ dependencies: "@types/sizzle" "*" +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/pako@^2.0.3": version "2.0.4" resolved "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz" @@ -276,6 +362,26 @@ abortcontroller-polyfill@^1.7.5: resolved "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.8.tgz" integrity sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.16.0: + version "8.16.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +ajv@^6.14.0: + version "6.14.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz" + integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + animation-frame-polyfill@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/animation-frame-polyfill/-/animation-frame-polyfill-1.0.2.tgz" @@ -301,6 +407,11 @@ autocompleter@^7.0.1: resolved "https://registry.npmjs.org/autocompleter/-/autocompleter-7.1.0.tgz" integrity sha512-uCToOnq7eAD/GJAteDbYuQ7ksDtrYWOy5CIAq43wh0dT+5frMpPlyD9tp+y5fz8KIcsP+zR2MjzoTAdW5aJESw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base64-arraybuffer@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz" @@ -331,11 +442,18 @@ bootstrap-select@~1.13.18: resolved "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.18.tgz" integrity sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow== -bootstrap@^5.3.3: +bootstrap@^5.3.3, bootstrap@>=3.0.0: version "5.3.3" resolved "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz" integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg== +brace-expansion@^5.0.2: + version "5.0.3" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz" + integrity sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA== + dependencies: + balanced-match "^4.0.2" + browser-cookies@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/browser-cookies/-/browser-cookies-1.2.0.tgz" @@ -392,6 +510,15 @@ create-point-cb@^1.0.0: dependencies: type-func "^1.0.1" +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crossvent@1.5.5: version "1.5.5" resolved "https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz" @@ -450,7 +577,7 @@ datatables.net-colreorder-bs5@~2.1.1: datatables.net-colreorder "2.1.2" jquery ">=1.7" -datatables.net-colreorder@2.1.2, datatables.net-colreorder@~2.1.1: +datatables.net-colreorder@~2.1.1, datatables.net-colreorder@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/datatables.net-colreorder/-/datatables.net-colreorder-2.1.2.tgz" integrity sha512-lIsUyOt2nBm4sD2cSzDKZcIVrGgrZkh90Z2f03s8p7DYcZSfXMHAhFBrDYf9/eAK6wJnODN8EDMsrtPHfgoSXA== @@ -475,7 +602,7 @@ datatables.net-fixedheader-bs5@~4.0.3: datatables.net-fixedheader "4.0.5" jquery ">=1.7" -datatables.net-fixedheader@4.0.5, datatables.net-fixedheader@~4.0.3: +datatables.net-fixedheader@~4.0.3, datatables.net-fixedheader@4.0.5: version "4.0.5" resolved "https://registry.npmjs.org/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.5.tgz" integrity sha512-cobQhOhjzqIYXTvMRrHUulULS8Re+hd2mmgFiOGKcZwHV0mofIwBlgiU3Ol4LHikHUCvsGnTEXoI+C7Ozma5sA== @@ -509,7 +636,7 @@ datatables.net-staterestore-dt@~1.4.2: datatables.net-staterestore "1.4.3" jquery ">=1.7" -datatables.net-staterestore@1.4.3, datatables.net-staterestore@~1.4.2: +datatables.net-staterestore@~1.4.2, datatables.net-staterestore@1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/datatables.net-staterestore/-/datatables.net-staterestore-1.4.3.tgz" integrity sha512-XSkCHwi+MZ8C5ZbZ1qlvIdIOs8YEJX4BVOk3GUMoSIta6xD4UsKTDV0SxfJWRYsNnDQwvCibQD0yJhK4Vk4xTw== @@ -517,7 +644,7 @@ datatables.net-staterestore@1.4.3, datatables.net-staterestore@~1.4.2: datatables.net "1.11 - 2" jquery ">=1.7" -"datatables.net@1.11 - 2", datatables.net@2.3.6, datatables.net@^2, datatables.net@^2.1.8: +datatables.net@^2, datatables.net@^2.1.8, "datatables.net@1.11 - 2", datatables.net@2.3.6: version "2.3.6" resolved "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.6.tgz" integrity sha512-xQ/dCxrjfxM0XY70wSIzakkTZ6ghERwlLmAPyCnu8Sk5cyt9YvOVyOsFNOa/BZ/lM63Q3i2YSSvp/o7GXZGsbg== @@ -531,6 +658,18 @@ datatables.net@2.2.1: dependencies: jquery ">=1.7" +debug@^4.3.1, debug@^4.3.2: + version "4.4.3" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deepmerge@^4.2.2: version "4.3.1" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" @@ -574,7 +713,7 @@ dom-set@^1.0.1: is-array "^1.0.1" iselement "^1.1.4" -dompurify@^3.0.5, dompurify@^3.2.4: +dompurify@^3.0.5, dompurify@^3.2.4, dompurify@^3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz" integrity sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q== @@ -602,6 +741,100 @@ echarts@~6.0.0: tslib "2.3.0" zrender "6.0.0" +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^9.1.1: + version "9.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.1.tgz" + integrity sha512-GaUN0sWim5qc8KVErfPBWmc31LEsOkrUJbvJZV+xuL3u2phMUK4HIvXlWAakfC8W4nzlK+chPEAkYOYb5ZScIw== + dependencies: + "@types/esrecurse" "^4.3.1" + "@types/estree" "^1.0.8" + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz" + integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== + +eslint@^10.0.2, "eslint@^6.0.0 || ^7.0.0 || >=8.0.0": + version "10.0.2" + resolved "https://registry.npmjs.org/eslint/-/eslint-10.0.2.tgz" + integrity sha512-uYixubwmqJZH+KLVYIVKY1JQt7tysXhtj21WSvjcSmU5SVNzMus1bgLe+pAt816yQ8opKfheVVoPLqvVMGejYw== + dependencies: + "@eslint-community/eslint-utils" "^4.8.0" + "@eslint-community/regexpp" "^4.12.2" + "@eslint/config-array" "^0.23.2" + "@eslint/config-helpers" "^0.5.2" + "@eslint/core" "^1.1.0" + "@eslint/plugin-kit" "^0.6.0" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + ajv "^6.14.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^9.1.1" + eslint-visitor-keys "^5.0.1" + espree "^11.1.1" + esquery "^1.7.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + minimatch "^10.2.1" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^11.1.1: + version "11.1.1" + resolved "https://registry.npmjs.org/espree/-/espree-11.1.1.tgz" + integrity sha512-AVHPqQoZYc+RUM4/3Ly5udlZY/U4LS8pIG05jEjWM2lQMU/oaZ7qshzAl2YP1tfNmXfftH3ohurfwNAug+MnsQ== + dependencies: + acorn "^8.16.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^5.0.1" + +esquery@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -612,7 +845,7 @@ eventemitter3@^5.0.1: resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -627,6 +860,16 @@ fast-json-patch@^3.1.1: resolved "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz" integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fast-png@^6.2.0: version "6.4.0" resolved "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz" @@ -648,10 +891,38 @@ fflate@^0.8.1: resolved "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== -formiojs@~4.17.4: - version "4.17.5" - resolved "https://registry.npmjs.org/formiojs/-/formiojs-4.17.5.tgz" - integrity sha512-vJW41GYhpJzmqYWJII8s48aZt+HQuYkh2jbqIQQtmQ6c4Eb1wlCXSrUNE2XeBmVCgEaC6YDHqnnvdhakZAAOOg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +formiojs@4.17.4: + version "4.17.4" + resolved "https://registry.npmjs.org/formiojs/-/formiojs-4.17.4.tgz" + integrity sha512-1wUWPLKTJ6/FWa5jCtw5YfsUzgr85TYh8aMhXUWxxRNBtYxZMLg4RcFsArqDJP9Lfh9z4N8p/sLSaOik6iu7kA== dependencies: "@formio/bootstrap3" "2.12.4-rc.1" "@formio/choices.js" "10.2.0" @@ -696,6 +967,13 @@ fuse.js@^6.6.2: resolved "https://registry.npmjs.org/fuse.js/-/fuse.js-6.6.2.tgz" integrity sha512-cJaJkxCCxC8qIIcPBF9yGxY0W/tVZS3uEISDxhYIdtk8OL93pe+6Zj7LjCqVV4dzbqcriOZ+kQ/NE4RXZHsIGA== +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + handlebars@~4.7.8: version "4.7.8" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz" @@ -728,11 +1006,21 @@ idb@^7.1.1: resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" @@ -748,6 +1036,18 @@ is-array@^1.0.1: resolved "https://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz" integrity sha512-gxiZ+y/u67AzpeFmAmo4CbtME/bs7J2C++su5zQzvQyaxUqVzkh69DI+jN+KZuSO6JaH6TIIU6M6LhqxMjxEpw== +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" @@ -758,6 +1058,11 @@ iselement@^1.1.4: resolved "https://registry.npmjs.org/iselement/-/iselement-1.1.4.tgz" integrity sha512-4Q519eWmbHO1pbimiz7H1iJRUHVmAmfh0viSsUD+oAwVO4ntZt7gpf8i8AShVBTyOvRTZNYNBpUxOIvwZR+ffw== +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + ismobilejs@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz" @@ -792,21 +1097,36 @@ jquery-validation-unobtrusive@^4.0.0: jquery "^3.6.0" jquery-validation ">=1.19" -jquery-validation@>=1.19, jquery-validation@^1.21.0: +jquery-validation@^1.21.0, jquery-validation@>=1.19: version "1.21.0" resolved "https://registry.npmjs.org/jquery-validation/-/jquery-validation-1.21.0.tgz" integrity sha512-xNot0rlUIgu7duMcQ5qb6MGkGL/Z1PQaRJQoZAURW9+a/2PGOUxY36o/WyNeP2T9R6jvWB8Z9lUVvvQWI/Zs5w== -jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.2.6, "jquery@>=1.5.0 <4.0", jquery@>=1.6, jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@^3.6.0, jquery@~3.7.1: +"jquery@^1.7 || ^2.0 || ^3.1", jquery@^3.6.0, jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.2.6, "jquery@>=1.5.0 <4.0", jquery@>=1.6, jquery@>=1.7, jquery@>=1.7.2, "jquery@>=3.4.0 <4.0.0", jquery@~3.7.1, "jquery@1.9.1 - 3": version "3.7.1" resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-logic-js@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.2.tgz" integrity sha512-ZBtBdMJieqQcH7IX/LaBsr5pX+Y5JIW+EhejtM3Ffg2jdN9Iwf+Ht6TbHnvAZ/YtwyuhPaCBlnvzrwVeWdvGDQ== +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + jspdf@~4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jspdf/-/jspdf-4.0.0.tgz" @@ -846,6 +1166,21 @@ jwt-decode@^3.1.2: resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz" integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + lie@~3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" @@ -853,6 +1188,13 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash-es@^4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" @@ -885,6 +1227,13 @@ malihu-custom-scrollbar-plugin@^3.1.5: dependencies: jquery-mousewheel ">=3.0.6" +minimatch@^10.2.1: + version "10.2.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz" + integrity sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw== + dependencies: + brace-expansion "^5.0.2" + minimist@^1.2.5: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" @@ -902,11 +1251,21 @@ moment@^2.29.4, moment@^2.30.1, moment@^2.9.0: resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + native-promise-only@^0.8.1: version "0.8.1" resolved "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz" integrity sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" @@ -919,6 +1278,32 @@ node-fetch@~2.6.1: dependencies: whatwg-url "^5.0.0" +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + pako@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" @@ -934,16 +1319,31 @@ parchment@^3.0.0: resolved "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz" integrity sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -popper.js@~1.16.1: +popper.js@^1.16.1, popper.js@~1.16.1: version "1.16.1" resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz" integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -954,6 +1354,11 @@ pubsub-js@~1.9.5: resolved "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.5.tgz" integrity sha512-5MZ0I9i5JWVO7SizvOviKvZU2qaBbl2KQX150FAA+fJBwYpwOUId7aNygURWSdPzlsA/xZ/InUKXqBbzM0czTA== +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + quill-delta@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz" @@ -1030,6 +1435,18 @@ setimmediate@^1.0.5: resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + signature_pad@^4.1.4: version "4.1.6" resolved "https://registry.npmjs.org/signature_pad/-/signature_pad-4.1.6.tgz" @@ -1055,11 +1472,6 @@ stackblur-canvas@^2.0.0: resolved "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz" integrity sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ== -string-hash@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" - integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A== - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" @@ -1067,6 +1479,11 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +string-hash@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/string-hash/-/string-hash-1.1.3.tgz" + integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A== + svg-pathdata@^6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz" @@ -1130,6 +1547,13 @@ tslib@2.3.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-func@^1.0.1: version "1.0.3" resolved "https://registry.npmjs.org/type-func/-/type-func-1.0.3.tgz" @@ -1140,6 +1564,13 @@ uglify-js@^3.1.4: resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" @@ -1177,11 +1608,28 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + zrender@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz" From d3474e24cf903f7e4bba9cd0df5cec7aaba12b4f Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Fri, 27 Feb 2026 15:31:38 -0800 Subject: [PATCH 04/57] Added AI Scoring user. --- .../Assessments/AIScoringConstants.cs | 12 + .../Assessments/Assessment.cs | 10 +- .../GrantManagerDataSeederContributor.cs | 23 +- ..._AddIsAiAssessmentToAssessment.Designer.cs | 4575 +++++++++++++++++ ...227210826_AddIsAiAssessmentToAssessment.cs | 49 + .../GrantTenantDbContextModelSnapshot.cs | 6 +- 6 files changed, 4669 insertions(+), 6 deletions(-) create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AIScoringConstants.cs create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.Designer.cs create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.cs diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AIScoringConstants.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AIScoringConstants.cs new file mode 100644 index 000000000..d74b69ae8 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AIScoringConstants.cs @@ -0,0 +1,12 @@ +using System; + +namespace Unity.GrantManager.Assessments; + +public static class AIScoringConstants +{ + // Well-known fixed GUID for the AI Scoring Person record (one per tenant) + public static readonly Guid AiPersonId = new("00000000-0000-0000-0000-000000000001"); + public const string AiOidcSub = "ai-scoring"; + public const string AiDisplayName = "AI Scoring"; + public const string AiBadge = "AI"; +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/Assessment.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/Assessment.cs index e4c0edab9..3f573a3e6 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/Assessment.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/Assessment.cs @@ -27,11 +27,13 @@ public virtual Application Application public bool? ApprovalRecommended { get; set; } + public bool IsAiAssessment { get; set; } + public AssessmentState Status { get; private set; } - public int? FinancialAnalysis { get; set; } - public int? EconomicImpact { get; set; } - public int? InclusiveGrowth { get; set; } + public int? FinancialAnalysis { get; set; } + public int? EconomicImpact { get; set; } + public int? InclusiveGrowth { get; set; } public int? CleanGrowth { get; set; } @@ -86,4 +88,4 @@ private void OnReopened() EndDate = null; } } - + diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/GrantManagerDataSeederContributor.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/GrantManagerDataSeederContributor.cs index a3ee4b5a9..6291c358c 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/GrantManagerDataSeederContributor.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/GrantManagerDataSeederContributor.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using Unity.GrantManager.Applications; +using Unity.GrantManager.Assessments; using Unity.GrantManager.GrantApplications; +using Unity.GrantManager.Identity; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; @@ -9,7 +11,8 @@ namespace Unity.GrantManager; public class GrantManagerDataSeederContributor( - IApplicationStatusRepository applicationStatusRepository) : IDataSeedContributor, ITransientDependency + IApplicationStatusRepository applicationStatusRepository, + IPersonRepository personRepository) : IDataSeedContributor, ITransientDependency { public static class GrantApplicationStates { @@ -37,6 +40,7 @@ public async Task SeedAsync(DataSeedContext context) } await SeedApplicationStatusAsync(); + await SeedAiScoringPersonAsync(context.TenantId); } @@ -67,4 +71,21 @@ private async Task SeedApplicationStatusAsync() } } } + + private async Task SeedAiScoringPersonAsync(System.Guid? tenantId) + { + var existing = await personRepository.FirstOrDefaultAsync(p => p.Id == AIScoringConstants.AiPersonId); + if (existing == null) + { + await personRepository.InsertAsync(new Person + { + Id = AIScoringConstants.AiPersonId, + OidcSub = AIScoringConstants.AiOidcSub, + OidcDisplayName = AIScoringConstants.AiDisplayName, + FullName = AIScoringConstants.AiDisplayName, + Badge = AIScoringConstants.AiBadge, + TenantId = tenantId + }); + } + } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.Designer.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.Designer.cs new file mode 100644 index 000000000..c68720af3 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.Designer.cs @@ -0,0 +1,4575 @@ +// +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("20260227210826_AddIsAiAssessmentToAssessment")] + partial class AddIsAiAssessmentToAssessment + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "9.0.5") + .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("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("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("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("IndigenousOrgInd") + .HasColumnType("text"); + + b.Property("IsDuplicated") + .HasColumnType("boolean"); + + 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("Sector") + .HasColumnType("text"); + + b.Property("SectorSubSectorIndustryDesc") + .HasColumnType("text"); + + b.Property("SiteId") + .HasColumnType("uuid"); + + 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.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.ToTable("ApplicantAgents", (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("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.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.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("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("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("RenderFormIoToHtml") + .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.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("RenderedHTML") + .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.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.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.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.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.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("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("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.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.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.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/20260227210826_AddIsAiAssessmentToAssessment.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.cs new file mode 100644 index 000000000..1f02f8752 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Migrations/TenantMigrations/20260227210826_AddIsAiAssessmentToAssessment.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Unity.GrantManager.Migrations.TenantMigrations +{ + /// + public partial class AddIsAiAssessmentToAssessment : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsAiAssessment", + table: "Assessments", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AlterColumn( + name: "Prefix", + table: "ApplicationForms", + type: "character varying(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsAiAssessment", + table: "Assessments"); + + migrationBuilder.AlterColumn( + name: "Prefix", + table: "ApplicationForms", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(100)", + oldMaxLength: 100, + oldNullable: true); + } + } +} 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 be337f9d1..a7ab067ff 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 @@ -1667,7 +1667,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("numeric"); b.Property("Prefix") - .HasColumnType("text"); + .HasMaxLength(100) + .HasColumnType("character varying(100)"); b.Property("PreventPayment") .HasColumnType("boolean"); @@ -2136,6 +2137,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("InclusiveGrowth") .HasColumnType("integer"); + b.Property("IsAiAssessment") + .HasColumnType("boolean"); + b.Property("IsComplete") .HasColumnType("boolean"); From 8316a275fb49dfdd1d97deb6c78198671a8292c1 Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Fri, 27 Feb 2026 15:48:02 -0800 Subject: [PATCH 05/57] AB#31871: Add AI Scoring to Feature Flags --- .../GrantManagerFeaturesDefinitionProvider.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantManagerFeaturesDefinitionProvider.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantManagerFeaturesDefinitionProvider.cs index 4a983ed32..c829cf27e 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantManagerFeaturesDefinitionProvider.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/GrantManagerFeaturesDefinitionProvider.cs @@ -56,6 +56,12 @@ public override void Define(IFeatureDefinitionContext context) displayName: LocalizableString .Create("AI Reporting"), valueType: new ToggleStringValueType()); + + myGroup.AddFeature("Unity.AI.Scoring", + defaultValue: defaultValue, + displayName: LocalizableString + .Create("AI Scoring"), + valueType: new ToggleStringValueType()); } } } From ce760cb67827dc4a9693dc0499af77ad5d1aa0d6 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Fri, 27 Feb 2026 16:25:11 -0800 Subject: [PATCH 06/57] AB#32008 Sonar fix simplify docx paragraph extraction loop --- .../AI/TextExtractionService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index 8de6b180e..0f55a62e3 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -166,11 +166,11 @@ private string ExtractTextFromWordDocx(byte[] fileContent) using var document = new XWPFDocument(stream); var parts = new List(); - foreach (var paragraph in document.Paragraphs.Take(MaxDocxParagraphs)) + foreach (var paragraphText in document.Paragraphs.Take(MaxDocxParagraphs).Select(paragraph => paragraph.ParagraphText)) { - if (!string.IsNullOrWhiteSpace(paragraph.ParagraphText)) + if (!string.IsNullOrWhiteSpace(paragraphText)) { - parts.Add(paragraph.ParagraphText); + parts.Add(paragraphText); } } From 608863d206734c26e7c56d662ff6dd9694f12221 Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Fri, 27 Feb 2026 16:25:17 -0800 Subject: [PATCH 07/57] AB#32021: Added AI Scoring to permissions --- .../GrantApplicationPermissionDefinitionProvider.cs | 5 +++++ .../Localization/GrantManager/en.json | 1 + .../Permissions/GrantApplicationPermissions.cs | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs index 428f8a5d6..54b25c9e1 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs @@ -130,6 +130,11 @@ public override void Define(IPermissionDefinitionContext context) GrantApplicationPermissions.AI.AttachmentSummary.Default, L("Permission:AI.AttachmentSummary")) .RequireFeatures("Unity.AI.AttachmentSummaries"); + + aiPermissionsGroup.AddPermission( + GrantApplicationPermissions.AI.ScoringAssistant.Default, + L("Permission:AI.ScoringAssistant")) + .RequireFeatures("Unity.AI.Scoring"); } private static LocalizableString L(string name) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json index 667bc316f..48cfff296 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json @@ -154,6 +154,7 @@ "Permission:AI.Reporting": "AI Reporting", "Permission:AI.ApplicationAnalysis": "AI Application Analysis", "Permission:AI.AttachmentSummary": "AI Attachment Summary", + "Permission:AI.ScoringAssistant": "AI Scoring Assistant", "ApplicationForms": "Forms", "ApplicationForms:Description": "Description", diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs index b46f5c310..4ce0b818e 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs @@ -58,6 +58,11 @@ public static class AttachmentSummary { public const string Default = GroupName + ".AttachmentSummary"; } + + public static class ScoringAssistant + { + public const string Default = GroupName + ".ScoringAssistant"; + } } public static class Assignments From 5d02ba95714fd89f72a7473688051633bc06f2b8 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Mon, 2 Mar 2026 10:05:41 -0800 Subject: [PATCH 08/57] AB#32008 Simplify text extraction async flow and stale comments --- .../AI/TextExtractionService.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index 0f55a62e3..88080d6a3 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -29,12 +29,12 @@ public TextExtractionService(ILogger logger) _logger = logger; } - public async Task ExtractTextAsync(string fileName, byte[] fileContent, string contentType) + public Task ExtractTextAsync(string fileName, byte[] fileContent, string contentType) { if (fileContent == null || fileContent.Length == 0) { _logger.LogDebug("File content is empty for {FileName}", fileName); - return string.Empty; + return Task.FromResult(string.Empty); } try @@ -50,14 +50,14 @@ public async Task ExtractTextAsync(string fileName, byte[] fileContent, extension == ".json" || extension == ".xml") { - rawText = await ExtractTextFromTextFileAsync(fileContent); - return NormalizeAndLimitText(rawText, fileName); + rawText = ExtractTextFromTextFile(fileContent); + return Task.FromResult(NormalizeAndLimitText(rawText, fileName)); } if (normalizedContentType.Contains("pdf") || extension == ".pdf") { rawText = ExtractTextFromPdfFile(fileName, fileContent); - return NormalizeAndLimitText(rawText, fileName); + return Task.FromResult(NormalizeAndLimitText(rawText, fileName)); } if (normalizedContentType.Contains("word") || @@ -69,11 +69,11 @@ public async Task ExtractTextAsync(string fileName, byte[] fileContent, if (extension == ".docx" || normalizedContentType.Contains("officedocument.wordprocessingml")) { rawText = ExtractTextFromWordDocx(fileContent); - return NormalizeAndLimitText(rawText, fileName); + return Task.FromResult(NormalizeAndLimitText(rawText, fileName)); } _logger.LogDebug("Legacy .doc extraction is not supported for {FileName}", fileName); - return string.Empty; + return Task.FromResult(string.Empty); } if (normalizedContentType.Contains("excel") || @@ -82,21 +82,21 @@ public async Task ExtractTextAsync(string fileName, byte[] fileContent, extension == ".xlsx") { rawText = ExtractTextFromExcelFile(fileName, fileContent); - return NormalizeAndLimitText(rawText, fileName); + return Task.FromResult(NormalizeAndLimitText(rawText, fileName)); } _logger.LogDebug("No text extraction available for content type {ContentType} with extension {Extension}", contentType, extension); - return string.Empty; + return Task.FromResult(string.Empty); } catch (Exception ex) { _logger.LogError(ex, "Error extracting text from {FileName}", fileName); - return string.Empty; + return Task.FromResult(string.Empty); } } - private async Task ExtractTextFromTextFileAsync(byte[] fileContent) + private string ExtractTextFromTextFile(byte[] fileContent) { try { @@ -113,7 +113,7 @@ private async Task ExtractTextFromTextFileAsync(byte[] fileContent) _logger.LogDebug("Truncated text content to {MaxLength} characters", MaxExtractedTextLength); } - return await Task.FromResult(text); + return text; } catch (Exception ex) { From dcd023bb4054be6719cab5376fad65e186e927d9 Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Tue, 3 Mar 2026 14:09:28 -0800 Subject: [PATCH 09/57] Created the handler. --- .../Assessments/AssessmentDto.cs | 1 + .../Assessments/AssessmentListItemDto.cs | 3 +- .../AiScoresheetAnswersGeneratedEvent.cs | 8 ++++ .../Handlers/CreateAiAssessmentHandler.cs | 40 +++++++++++++++++ .../Handlers/GenerateAISummaryHandler.cs | 19 +++++--- .../Assessments/AssessmentManager.cs | 43 +++++++++++++++++++ .../AssessmentWithAssessorQueryResultItem.cs | 9 ++-- 7 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Events/AiScoresheetAnswersGeneratedEvent.cs create mode 100644 applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/CreateAiAssessmentHandler.cs diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentDto.cs index f11a22cc5..3dc9eb44c 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentDto.cs @@ -15,6 +15,7 @@ public class AssessmentDto : EntityDto public AssessmentState Status { get; set; } public bool IsComplete { get; set; } public bool? ApprovalRecommended { get; set; } + public bool IsAiAssessment { get; set; } } } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentListItemDto.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentListItemDto.cs index 9cb78cee7..f1e9606f6 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentListItemDto.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Assessments/AssessmentListItemDto.cs @@ -17,7 +17,8 @@ public class AssessmentListItemDto public AssessmentState Status { get; set; } public bool IsComplete { get; set; } public bool? ApprovalRecommended { get; set; } - + public bool IsAiAssessment { get; set; } + public double SubTotal { get; set; } public int? FinancialAnalysis { get; set; } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Events/AiScoresheetAnswersGeneratedEvent.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Events/AiScoresheetAnswersGeneratedEvent.cs new file mode 100644 index 000000000..2090524f7 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Events/AiScoresheetAnswersGeneratedEvent.cs @@ -0,0 +1,8 @@ +using Unity.GrantManager.Applications; + +namespace Unity.GrantManager.Intakes.Events; + +public class AiScoresheetAnswersGeneratedEvent +{ + public Application? Application { get; set; } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/CreateAiAssessmentHandler.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/CreateAiAssessmentHandler.cs new file mode 100644 index 000000000..a69723573 --- /dev/null +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/CreateAiAssessmentHandler.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; +using Unity.GrantManager.Assessments; +using Unity.GrantManager.Intakes.Events; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; +using Volo.Abp.Features; + +namespace Unity.GrantManager.Intakes.Handlers; + +public class CreateAiAssessmentHandler( + AssessmentManager assessmentManager, + IFeatureChecker featureChecker, + ILogger logger) : ILocalEventHandler, ITransientDependency +{ + public async Task HandleEventAsync(AiScoresheetAnswersGeneratedEvent eventData) + { + if (eventData?.Application == null) + { + logger.LogWarning("Event data or application is null in CreateAiAssessmentHandler."); + return; + } + + if (!await featureChecker.IsEnabledAsync("Unity.AI.Scoring")) + { + return; + } + + try + { + await assessmentManager.CreateAiAssessmentAsync(eventData.Application); + logger.LogInformation("Created AI assessment for application {ApplicationId}.", eventData.Application.Id); + } + catch (Exception ex) + { + logger.LogError(ex, "Error creating AI assessment for application {ApplicationId}.", eventData.Application.Id); + } + } +} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/GenerateAISummaryHandler.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/GenerateAISummaryHandler.cs index dbc69472b..9cc78e9dc 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/GenerateAISummaryHandler.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Intakes/Handlers/GenerateAISummaryHandler.cs @@ -8,6 +8,7 @@ using Unity.GrantManager.Intakes.Events; using Volo.Abp.DependencyInjection; using Volo.Abp.EventBus; +using Volo.Abp.EventBus.Local; using Unity.Flex.Domain.Scoresheets; using System.Text.Json; using Volo.Abp.Features; @@ -28,6 +29,7 @@ public class GenerateAiSummaryHandler : ILocalEventHandler + /// Creates and inserts a read-only AI Scoring for an application. + /// No user is required — the well-known AI Scoring Person is used as the assessor. + /// Does not affect application state. Idempotent: returns the existing record if one already exists. + /// + /// The application being assessed. + /// A new or existing AI for the . + public async Task CreateAiAssessmentAsync(Application application) + { + // Idempotency: only one AI assessment per application + var existing = await _assessmentRepository.FirstOrDefaultAsync( + x => x.ApplicationId == application.Id && x.IsAiAssessment); + if (existing != null) + { + return existing; + } + + var form = await _applicationFormRepository.GetAsync(application.ApplicationFormId); + + var newAssessment = new Assessment( + GuidGenerator.Create(), + application.Id, + AIScoringConstants.AiPersonId) + { + IsAiAssessment = true + }; + + var assessment = await _assessmentRepository.InsertAsync(newAssessment, autoSave: true); + + if (form.ScoresheetId != null && await _featureChecker.IsEnabledAsync("Unity.Flex")) + { + await _localEventBus.PublishAsync(new CreateScoresheetInstanceEto() + { + ScoresheetId = form.ScoresheetId ?? Guid.Empty, + CorrelationId = assessment.Id, + CorrelationProvider = "Assessment", + RelatedCorrelationId = null + }); + } + + return assessment; + } + /// /// Checks if a user has already been assigned an for an . /// diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AssessmentWithAssessorQueryResultItem.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AssessmentWithAssessorQueryResultItem.cs index 28640316b..99a8cdbd8 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AssessmentWithAssessorQueryResultItem.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain/Assessments/AssessmentWithAssessorQueryResultItem.cs @@ -16,9 +16,10 @@ public class AssessmentWithAssessorQueryResultItem public AssessmentState Status { get; set; } public bool IsComplete { get; set; } public bool? ApprovalRecommended { get; set; } - public int? FinancialAnalysis { get; set; } - public int? EconomicImpact { get; set; } - public int? InclusiveGrowth { get; set; } + public bool IsAiAssessment { get; set; } + public int? FinancialAnalysis { get; set; } + public int? EconomicImpact { get; set; } + public int? InclusiveGrowth { get; set; } public int? CleanGrowth { get; set; } } - + From e943f05f6ff880a460603e510206c98a94c66aa3 Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Tue, 3 Mar 2026 14:10:14 -0800 Subject: [PATCH 10/57] Read-Only Enforcement --- .../Assessments/AssessmentAppService.cs | 18 ++++++++++++++++++ .../AssessmentAuthorizationHandler.cs | 6 ++++++ .../GrantManagerDomainErrorCodes.cs | 1 + 3 files changed, 25 insertions(+) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs index 75ac9b3c6..4fc3b5302 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Authorization; +using Volo.Abp; using Microsoft.AspNetCore.Authorization.Infrastructure; using System; using System.Collections.Generic; @@ -224,6 +225,10 @@ public async Task UpdateAssessmentRecommendation(UpdateAssessmentRecommendationD var assessment = await _assessmentRepository.GetAsync(dto.AssessmentId); if (assessment != null) { + if (assessment.IsAiAssessment) + { + throw new BusinessException(GrantManagerDomainErrorCodes.CannotModifyAiAssessment); + } assessment.ApprovalRecommended = dto.ApprovalRecommended; await _assessmentRepository.UpdateAsync(assessment); } @@ -278,6 +283,11 @@ public async Task ExecuteAssessmentAction(Guid assessmentId, Asse { var assessment = await _assessmentRepository.GetAsync(assessmentId); + if (assessment.IsAiAssessment) + { + throw new BusinessException(GrantManagerDomainErrorCodes.CannotModifyAiAssessment); + } + await AuthorizationService.CheckAsync(assessment, GetActionAuthorizationRequirement(triggerAction)); await ApplyAdditionalValidationsAsync(assessmentId, triggerAction); @@ -332,6 +342,10 @@ public async Task UpdateAssessmentScore(AssessmentScoresDto dto) var assessment = await _assessmentRepository.GetAsync(dto.AssessmentId); if (assessment != null) { + if (assessment.IsAiAssessment) + { + throw new BusinessException(GrantManagerDomainErrorCodes.CannotModifyAiAssessment); + } if (CurrentUser.GetId() != assessment.AssessorId) { throw new AbpValidationException("Error: You do not own this assessment record."); @@ -364,6 +378,10 @@ public async Task SaveScoresheetSectionAnswers(AssessmentScoreSectionDto dto) { if (assessment != null) { + if (assessment.IsAiAssessment) + { + throw new BusinessException(GrantManagerDomainErrorCodes.CannotModifyAiAssessment); + } if (CurrentUser.GetId() != assessment.AssessorId) { throw new AbpValidationException("Error: You do not own this assessment record."); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAuthorizationHandler.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAuthorizationHandler.cs index 609d3fc84..3a8a4bbff 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAuthorizationHandler.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAuthorizationHandler.cs @@ -24,6 +24,12 @@ protected override async Task HandleRequirementAsync( OperationAuthorizationRequirement requirement, Assessment resource) { + if (resource.IsAiAssessment) + { + context.Fail(); + return; + } + if (requirement.Name.Equals(UnitySelector.Review.AssessmentReviewList.Update.SendBack) && await CheckPolicyAsync(requirement.Name, context)) { diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/GrantManagerDomainErrorCodes.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/GrantManagerDomainErrorCodes.cs index b06c20bc5..df6ebda43 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/GrantManagerDomainErrorCodes.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/GrantManagerDomainErrorCodes.cs @@ -12,6 +12,7 @@ public static class GrantManagerDomainErrorCodes /* ASSESSMENTS */ public const string AssessmentNotFound = "GrantManager:AssessmentNotFound"; + public const string CannotModifyAiAssessment = "GrantManager:CannotModifyAiAssessment"; public const string AssessmentUserAssignmentAlreadyExists = "GrantManager:AssessmentUserAssignmentAlreadyExists"; public const string CantCreateAssessmentForClosedApplication = "GrantManager:CantCreateAssessmentForClosedApplication"; public const string CantUpdateAssessmentForClosedApplication = "GrantManager:CantUpdateAssessmentForClosedApplication"; From 7c3c06dfec83ad9f381af9f7e0276097add76169 Mon Sep 17 00:00:00 2001 From: JamesPasta Date: Tue, 3 Mar 2026 15:58:59 -0800 Subject: [PATCH 11/57] feature/AB#31620-SentToFSB --- .../Codes/CasPaymentRequestStatus.cs | 1 + .../Domain/Services/PaymentsManager.cs | 2 ++ .../Repositories/PaymentRequestRepository.cs | 2 ++ .../CasPaymentRequestCoordinator.cs | 8 ++++- .../Pages/PaymentRequests/Index.js | 32 ++++++++++++------- .../History/PaymentHistoryAppService.cs | 20 +++++++++++- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application.Contracts/Codes/CasPaymentRequestStatus.cs b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application.Contracts/Codes/CasPaymentRequestStatus.cs index e10b259c9..3b292dda5 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application.Contracts/Codes/CasPaymentRequestStatus.cs +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application.Contracts/Codes/CasPaymentRequestStatus.cs @@ -4,6 +4,7 @@ public static class CasPaymentRequestStatus { // Unity Status public const string SentToCas = "SentToCas"; + public const string SentToAccountsPayable = "SentToAccountsPayable"; // CAS INVOICE STATUS public const string ErrorFromCas = "Error"; diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/Domain/Services/PaymentsManager.cs b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/Domain/Services/PaymentsManager.cs index 73bd07d3c..31ebc7326 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/Domain/Services/PaymentsManager.cs +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/Domain/Services/PaymentsManager.cs @@ -9,6 +9,7 @@ using Unity.Payments.Domain.Shared; using Unity.Payments.Domain.Workflow; using Unity.Payments.Enums; +using Unity.Payments.Codes; using Unity.Payments.PaymentRequests; using Unity.Payments.Permissions; using Volo.Abp.Authorization.Permissions; @@ -150,6 +151,7 @@ public async Task TriggerAction(Guid paymentRequestsId, PaymentA if (preventPayment) { statusChangedTo = PaymentRequestStatus.FSB; + paymentRequest.SetInvoiceStatus(CasPaymentRequestStatus.SentToAccountsPayable); } else { diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/EntityFrameworkCore/Repositories/PaymentRequestRepository.cs b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/EntityFrameworkCore/Repositories/PaymentRequestRepository.cs index afee92a5b..be9999cd1 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/EntityFrameworkCore/Repositories/PaymentRequestRepository.cs +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/EntityFrameworkCore/Repositories/PaymentRequestRepository.cs @@ -22,6 +22,8 @@ public PaymentRequestRepository(IDbContextProvider dbContextP { ReCheckStatusList.Add(CasPaymentRequestStatus.ServiceUnavailable); ReCheckStatusList.Add(CasPaymentRequestStatus.SentToCas); + ReCheckStatusList.Add(CasPaymentRequestStatus.NotFound); + ReCheckStatusList.Add(CasPaymentRequestStatus.SentToAccountsPayable); ReCheckStatusList.Add(CasPaymentRequestStatus.NeverValidated); FailedStatusList.Add(CasPaymentRequestStatus.ServiceUnavailable); diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/PaymentRequests/CasPaymentRequestCoordinator.cs b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/PaymentRequests/CasPaymentRequestCoordinator.cs index 84b8cc8fa..7e2e287be 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/PaymentRequests/CasPaymentRequestCoordinator.cs +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Application/PaymentRequests/CasPaymentRequestCoordinator.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Unity.Payments.Domain.PaymentRequests; using System; +using System.Linq; using Volo.Abp.Application.Services; using System.Collections.Generic; using Volo.Abp.TenantManagement; @@ -8,7 +9,7 @@ using Volo.Abp.Uow; using Microsoft.Extensions.Logging; using Unity.Payments.Integrations.Cas; -using System.Linq; +using Unity.Payments.Codes; using Unity.Payments.RabbitMQ.QueueMessages; using Unity.Notifications.Integrations.RabbitMQ; @@ -138,6 +139,11 @@ public async Task AddPaymentRequestsToReconciliationQueue() paymentReqeust = await _paymentRequestsRepository.GetAsync(PaymentRequestId); if (paymentReqeust != null) { + if(paymentReqeust.InvoiceStatus == CasPaymentRequestStatus.NotFound && result.InvoiceStatus == CasPaymentRequestStatus.NotFound) + { + result.InvoiceStatus = CasPaymentRequestStatus.NotFound+"2"; + } + paymentReqeust.SetInvoiceStatus(result.InvoiceStatus ?? ""); paymentReqeust.SetPaymentStatus(result.PaymentStatus ?? ""); paymentReqeust.SetPaymentDate(result.PaymentDate ?? ""); diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js index 945406074..2da0a4154 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js @@ -186,10 +186,16 @@ $(function () { payment_approve_buttons.disable(); payment_check_status_buttons.disable(); + history_button.disable(); dataTable.on('search.dt', () => handleSearch()); - function checkAllRowsHaveState(state) { - return dataTable.rows('.selected').data().toArray().every(row => row.status === state); + function checkAllRowsHaveState(states) { + const allowedStates = Array.isArray(states) ? states : [states]; + return dataTable + .rows('.selected') + .data() + .toArray() + .every(row => allowedStates.includes(row.status)); } $('#PaymentRequestListTable').on('click', 'tr td', function (e) { @@ -256,13 +262,13 @@ $(function () { } function checkActionButtons() { - let isOnlySubmittedToCas = checkAllRowsHaveState('Submitted'); - if (isOnlySubmittedToCas) { + let isInSentState = checkAllRowsHaveState(['Submitted', 'FSB']); + if (isInSentState) { payment_check_status_buttons.enable(); } else { payment_check_status_buttons.disable(); } - if (dataTable.rows({ selected: true }).indexes().length > 0 && !isOnlySubmittedToCas) { + if (dataTable.rows({ selected: true }).indexes().length > 0 && !isInSentState) { if (abp.auth.isGranted('PaymentsPermissions.Payments.L1ApproveOrDecline') || abp.auth.isGranted('PaymentsPermissions.Payments.L2ApproveOrDecline') || abp.auth.isGranted('PaymentsPermissions.Payments.L3ApproveOrDecline')) { @@ -272,15 +278,11 @@ $(function () { payment_approve_buttons.disable(); } - if (dataTable.rows({ selected: true }).indexes().length == 1) { - history_button.enable(); - } else { - history_button.disable(); - } + checkEnableHistoryButton(dataTable, history_button); } else { payment_approve_buttons.disable(); - history_button.enable(); + checkEnableHistoryButton(dataTable, history_button); } } @@ -792,6 +794,14 @@ let casPaymentResponseModal = new abp.ModalManager({ viewUrl: '../PaymentRequests/CasPaymentRequestResponse' }); +function checkEnableHistoryButton(dataTable, history_button) { + if (dataTable.rows({ selected: true }).indexes().length == 1) { + history_button.enable(); + } else { + history_button.disable(); + } +} + function openCasResponseModal(casResponse) { casPaymentResponseModal.open({ casResponse: casResponse diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs index db660367e..0be21bf6a 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs @@ -15,6 +15,12 @@ public async Task> GetPaymentHistoryList(Guid? entityId) { List historyList = []; CancellationToken cancellationToken = default; + + if (entityId == null || entityId == Guid.Empty) + { + return historyList; + } + var entityChanges = await extendedAuditLogRepository.GetEntityChangeByTypeWithUsernameAsync( entityId, HistoryConsts.PaymentEntityTypeFullNames, @@ -23,17 +29,29 @@ public async Task> GetPaymentHistoryList(Guid? entityId) foreach (var entityChange in entityChanges) { + // Add explicit filter to ensure only matching entityId records + if (entityChange.EntityChange.EntityId != entityId.ToString()) + { + continue; + } + foreach (var propertyChange in entityChange.EntityChange.PropertyChanges) { string origninalValue = CleanValue(propertyChange.OriginalValue); string newValue = CleanValue(propertyChange.NewValue); string displayNewValue = MapFsbToDisplayText(newValue); + + // Don't display history if both original and new values are empty, as it doesn't provide useful information and may clutter the history with irrelevant entries. + if (string.IsNullOrEmpty(origninalValue) && string.IsNullOrEmpty(newValue)) + { + continue; + } int changeType = (int)entityChange.EntityChange.ChangeType; DateTime utcDateTime = DateTime.SpecifyKind(entityChange.EntityChange.ChangeTime, DateTimeKind.Utc); HistoryDto historyDto = new() { EntityName = GetShortEntityName(entityChange.EntityChange.EntityTypeFullName), - PropertyName = propertyChange.PropertyName, // The name of the property on the entity class. + PropertyName = propertyChange.PropertyName, OriginalValue = origninalValue, NewValue = displayNewValue, ChangeTime = utcDateTime.ToLocalTime(), From 1c918563ed7189b224643eced215a4e6bca0898a Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:26:23 -0800 Subject: [PATCH 12/57] AB#31822 - Enable DataTables fixed headers and scrollable bodies --- .../src/Pages/Identity/Roles/index.css | 5 ----- .../src/Pages/Identity/Roles/index.js | 3 ++- .../src/Pages/Identity/Users/index.css | 5 ----- .../src/Pages/Identity/Users/index.js | 3 ++- .../Pages/PaymentRequests/Index.js | 3 ++- .../wwwroot/themes/ux2/table-utils.js | 21 +++++++++++++++---- .../Pages/ApplicationForms/Index.css | 5 ----- .../Pages/ApplicationForms/Index.js | 1 + .../Pages/GrantApplications/Index.js | 1 + .../Pages/Intakes/Index.css | 5 ----- .../Pages/Intakes/Index.js | 3 ++- 11 files changed, 27 insertions(+), 28 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.css b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.css index 2abd758d1..d465d3b2d 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.css +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.css @@ -19,8 +19,3 @@ #IdentityRolesWrapper { background-color: transparent; } - -#IdentityRolesWrapper .dt-scroll-body { - max-height: calc(100vh - 370px); - overflow-y: scroll; -} diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.js b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.js index 74f5376cf..d54abb109 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.js +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Roles/index.js @@ -168,7 +168,8 @@ $(function () { dataTableName: 'IdentityRolesTable', dynamicButtonContainerId: 'dynamicButtonContainerId', useNullPlaceholder: true, - externalSearchId: 'search-roles' + externalSearchId: 'search-roles', + fixedHeaders: true }); _createModal.onResult(function () { diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.css b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.css index 3e2e3dfa9..26f3c597a 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.css +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.css @@ -28,8 +28,3 @@ #UsersWrapper { background-color: transparent; } - -#UsersWrapper .dt-scroll-body { - max-height: calc(100vh - 370px); - overflow-y: scroll; -} diff --git a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.js b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.js index 6cfa429cc..ca1c07d2e 100644 --- a/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.js +++ b/applications/Unity.GrantManager/modules/Unity.Identity.Web/src/Pages/Identity/Users/index.js @@ -266,7 +266,8 @@ $(function () { dataTableName: 'UsersTable', dynamicButtonContainerId: 'dynamicButtonContainerId', useNullPlaceholder: true, - externalSearchId: 'search-users' + externalSearchId: 'search-users', + fixedHeaders: true }); _editModal.onResult(function () { diff --git a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js index 945406074..8c7e63f4c 100644 --- a/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js +++ b/applications/Unity.GrantManager/modules/Unity.Payments/src/Unity.Payments.Web/Pages/PaymentRequests/Index.js @@ -164,7 +164,8 @@ $(function () { languageSetValues: {}, dataTableName: 'PaymentRequestListTable', dynamicButtonContainerId: 'dynamicButtonContainerId', - useNullPlaceholder: true + useNullPlaceholder: true, + fixedHeaders: true }); // Attach the draw event to add custom row coloring logic diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 2e9125f45..ddbad3bb9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -185,6 +185,11 @@ if ($.fn.dataTable !== undefined && $.fn.dataTable.Api) { * @param {string} [options.externalSearchId='search'] - ID of external search input element * @param {boolean} [options.disableColumnSelect=false] - Disable column visibility toggle * @param {Array} [options.listColumnDefs] - Additional columnDefs configurations + * @param {Function} [options.onStateSaveParams] - Hook for additional state save parameters + * @param {Function} [options.onStateLoadParams] - Hook for additional state load parameters + * @param {Function} [options.onStateLoaded] - Hook called after state is loaded + * @param {boolean} [options.fixedHeader=false] - Enable fixed header with scrollable body + * @param {string} [options.fixedHeaderOffset='calc(100vh - 325px)'] - CSS height for fixed header offset * @returns {DataTable} Initialized DataTable API instance * * @example @@ -216,9 +221,11 @@ function initializeDataTable(options) { externalSearchId = 'search', disableColumnSelect = false, listColumnDefs, - onStateSaveParams,//External hooks for save/load/loaded + onStateSaveParams, //External hooks for save/load/loaded onStateLoadParams, onStateLoaded, + fixedHeader = false, + fixedHeaderOffset = `calc(100vh - 325px)` } = options; // Process columns and visibility @@ -235,8 +242,8 @@ function initializeDataTable(options) { // Add loading class initially dt.closest('.dt-container, .dataTables_wrapper').addClass('dt-loading'); - // Create the DataTable - let iDt = new DataTable(dt, { + // Create the DataTable Configuration object + let configuration = { serverSide: serverSideEnabled, paging: pagingEnabled, order: defaultSortOrder, @@ -383,7 +390,13 @@ function initializeDataTable(options) { settings.oInit.onStateLoaded(dtApi, data); } }, - }); + }; + + if (fixedHeader) { + configuration.scrollY = fixedHeaderOffset; + } + + let iDt = new DataTable(dt, configuration); // Initialize FilterRow plugin initializeFilterRowPlugin(iDt); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.css index d3a841326..890f0aeb4 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.css +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.css @@ -12,11 +12,6 @@ margin: 0.25rem; } -#ApplicationFormsTable_wrapper .dt-scroll-body { - max-height: calc(100vh - 350px); - overflow-y: scroll; -} - #FormsManageDropdown a { cursor: pointer; } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.js index df0268574..05cd641f8 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/ApplicationForms/Index.js @@ -128,6 +128,7 @@ dynamicButtonContainerId: 'dynamicButtonContainerId', useNullPlaceholder: true, externalSearchId: 'search-forms', + fixedHeaders: true }); createModal.onResult(function () { diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Index.js index 4ab9b69a5..6f13ded7a 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Index.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/GrantApplications/Index.js @@ -396,6 +396,7 @@ $(function () { serverSideEnabled: false, pagingEnabled: true, reorderEnabled: true, + fixedHeaders: true, languageSetValues, dataTableName: 'GrantApplicationsTable', dynamicButtonContainerId: 'dynamicButtonContainerId', diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.css b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.css index 9c9f00933..e5f1d20fb 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.css +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.css @@ -11,8 +11,3 @@ #IntakesTable_filter input { margin: 0.25rem; } - -#IntakesTable_wrapper .dt-scroll-body { - max-height: calc(100vh - 350px); - overflow-y: scroll; -} diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.js index bac333052..18b6f5f0f 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Intakes/Index.js @@ -107,7 +107,8 @@ dataTableName: 'IntakesTable', dynamicButtonContainerId: 'dynamicButtonContainerId', useNullPlaceholder: true, - externalSearchId: 'search-intakes' + externalSearchId: 'search-intakes', + fixedHeaders: true }); createModal.onResult(function () { From a5187c835537c07e7a5d02734a9c48d24be0dcd5 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:34:25 -0800 Subject: [PATCH 13/57] AB#31822 - Update NPM dependencies and lockfile for latest patches --- .../src/Unity.GrantManager.Web/package.json | 12 +-- .../src/Unity.GrantManager.Web/yarn.lock | 75 +++++++++++-------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json index 63cd5b81d..6d80dfa65 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/package.json @@ -7,12 +7,12 @@ "@abp/font-awesome": "~9.1.3", "bootstrap-4": "npm:bootstrap@~4.6.2", "bootstrap-select": "~1.13.18", - "datatables.net-bs5": "~2.3.6", + "datatables.net-bs5": "~2.3.7", "datatables.net-buttons-bs5": "~3.2.6", "datatables.net-colreorder": "~2.1.1", "datatables.net-colreorder-bs5": "~2.1.1", - "datatables.net-fixedheader": "~4.0.3", - "datatables.net-fixedheader-bs5": "~4.0.3", + "datatables.net-fixedheader": "~4.0.6", + "datatables.net-fixedheader-bs5": "~4.0.6", "datatables.net-select-bs5": "~3.1.0", "datatables.net-staterestore": "~1.4.2", "datatables.net-staterestore-dt": "~1.4.2", @@ -20,17 +20,17 @@ "formiojs": "4.17.4", "html2canvas": "~1.4.1", "jquery-maskmoney": "~3.0.2", - "jspdf": "~4.0.0", + "jspdf": "~4.2.0", "jszip": "~3.10.1", "popper.js": "~1.16.1", "pubsub-js": "~1.9.5", - "sortablejs": "~1.15.6", + "sortablejs": "~1.15.7", "tributejs": "~5.1.3", "tinymce": "~8.3.2", "handlebars": "~4.7.8", "dompurify": "^3.3.1" }, "devDependencies": { - "@types/jquery": "~3.5.33" + "@types/jquery": "~3.5.34" } } \ No newline at end of file diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock index aa748a116..ad7891b43 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/yarn.lock @@ -193,7 +193,7 @@ dependencies: just-compare "^2.3.0" -"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.6", "@babel/runtime@^7.28.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.6", "@babel/runtime@^7.28.6", "@babel/runtime@^7.9.2": version "7.28.6" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz" integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== @@ -244,10 +244,10 @@ resolved "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz" integrity sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw== -"@types/jquery@~3.5.33": - version "3.5.33" - resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz" - integrity sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g== +"@types/jquery@~3.5.34": + version "3.5.34" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.34.tgz#c1993eaac0db03cf9db974976dd8f07bbf7c5708" + integrity sha512-3m3939S3erqmTLJANS/uy0B6V7BorKx7RorcGZVjZ62dF5PAGbKEDZK1CuLtKombJkFA2T1jl8LAIIs7IV6gBQ== dependencies: "@types/sizzle" "*" @@ -416,7 +416,7 @@ custom-event@^1.0.0: resolved "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz" integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== -datatables.net-bs5@^2, datatables.net-bs5@^2.1.8, datatables.net-bs5@~2.3.6: +datatables.net-bs5@^2, datatables.net-bs5@^2.1.8: version "2.3.6" resolved "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.3.6.tgz" integrity sha512-oUNGjZrpNC2fY3l/6V4ijTC9kyVKU4Raons+RFmq2J7590rPn0c+5WAYKBx0evgW/CW7WfhStGBrU7+WJig6Og== @@ -424,6 +424,14 @@ datatables.net-bs5@^2, datatables.net-bs5@^2.1.8, datatables.net-bs5@~2.3.6: datatables.net "2.3.6" jquery ">=1.7" +datatables.net-bs5@~2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/datatables.net-bs5/-/datatables.net-bs5-2.3.7.tgz#ddef957ee23b03c2d4bc1d48735b39c6182e5d53" + integrity sha512-RiCEMpMXDBeMDwjSrMpmcXDU6mibRMuOn7Wk7k3SlOfLEY3FQHO7S2m+K7teXYeaNlCLyjJMU+6BUUwlBCpLFw== + dependencies: + datatables.net "2.3.7" + jquery ">=1.7" + datatables.net-buttons-bs5@~3.2.6: version "3.2.6" resolved "https://registry.npmjs.org/datatables.net-buttons-bs5/-/datatables.net-buttons-bs5-3.2.6.tgz" @@ -466,19 +474,19 @@ datatables.net-colreorder@2.1.2, datatables.net-colreorder@~2.1.1: datatables.net "2.2.1" jquery ">=1.7" -datatables.net-fixedheader-bs5@~4.0.3: - version "4.0.5" - resolved "https://registry.npmjs.org/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-4.0.5.tgz" - integrity sha512-R0m4Mntda7wfRCpyjGS2RWFw2861X8e4trn6SnBHID2htuMPPdk11bK4RVJMipgFDxdMfJbvEMH5Hkx5XKrNuA== +datatables.net-fixedheader-bs5@~4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/datatables.net-fixedheader-bs5/-/datatables.net-fixedheader-bs5-4.0.6.tgz#25bc9d2d5f9ded665ea4915b26a433f74e3c2979" + integrity sha512-V5KhTssDq2osUG8aXur5wf8j6tXE9kSP/34C5k0DKIFkHjvZiK1yWPyadP6/T9JJRKWuJppPaLiJ1PzB+nlwPw== dependencies: datatables.net-bs5 "^2" - datatables.net-fixedheader "4.0.5" + datatables.net-fixedheader "4.0.6" jquery ">=1.7" -datatables.net-fixedheader@4.0.5, datatables.net-fixedheader@~4.0.3: - version "4.0.5" - resolved "https://registry.npmjs.org/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.5.tgz" - integrity sha512-cobQhOhjzqIYXTvMRrHUulULS8Re+hd2mmgFiOGKcZwHV0mofIwBlgiU3Ol4LHikHUCvsGnTEXoI+C7Ozma5sA== +datatables.net-fixedheader@4.0.6, datatables.net-fixedheader@~4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/datatables.net-fixedheader/-/datatables.net-fixedheader-4.0.6.tgz#0c361a8a90542d75402f897db401085433efcebe" + integrity sha512-icYg/qKDpqGDrAVRWfsjt0xQdngk48R7LWkS9t8kaZFp9c4xrLFcmmPtRLgPp5/S4JHZbbsxmVkF16kscjNZjg== dependencies: datatables.net "^2" jquery ">=1.7" @@ -531,6 +539,13 @@ datatables.net@2.2.1: dependencies: jquery ">=1.7" +datatables.net@2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-2.3.7.tgz#3cd34f6f5d1f40a46b5a20a4ba32604bdbcd6738" + integrity sha512-AvsjG/Nkp6OxeyBKYZauemuzQCPogE1kOtKwG4sYjvdqGCSLiGaJagQwXv4YxG+ts5vaJr6qKGG9ec3g6vTo3w== + dependencies: + jquery ">=1.7" + deepmerge@^4.2.2: version "4.3.1" resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" @@ -574,7 +589,7 @@ dom-set@^1.0.1: is-array "^1.0.1" iselement "^1.1.4" -dompurify@^3.0.5, dompurify@^3.2.4: +dompurify@^3.0.5, dompurify@^3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz" integrity sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q== @@ -648,10 +663,10 @@ fflate@^0.8.1: resolved "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz" integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A== -formiojs@~4.17.4: - version "4.17.5" - resolved "https://registry.npmjs.org/formiojs/-/formiojs-4.17.5.tgz" - integrity sha512-vJW41GYhpJzmqYWJII8s48aZt+HQuYkh2jbqIQQtmQ6c4Eb1wlCXSrUNE2XeBmVCgEaC6YDHqnnvdhakZAAOOg== +formiojs@4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/formiojs/-/formiojs-4.17.4.tgz#2f7ef167fbdd1ef6c6fa880122300e17b68156fe" + integrity sha512-1wUWPLKTJ6/FWa5jCtw5YfsUzgr85TYh8aMhXUWxxRNBtYxZMLg4RcFsArqDJP9Lfh9z4N8p/sLSaOik6iu7kA== dependencies: "@formio/bootstrap3" "2.12.4-rc.1" "@formio/choices.js" "10.2.0" @@ -807,18 +822,18 @@ json-logic-js@^2.0.2: resolved "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.2.tgz" integrity sha512-ZBtBdMJieqQcH7IX/LaBsr5pX+Y5JIW+EhejtM3Ffg2jdN9Iwf+Ht6TbHnvAZ/YtwyuhPaCBlnvzrwVeWdvGDQ== -jspdf@~4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jspdf/-/jspdf-4.0.0.tgz" - integrity sha512-w12U97Z6edKd2tXDn3LzTLg7C7QLJlx0BPfM3ecjK2BckUl9/81vZ+r5gK4/3KQdhAcEZhENUxRhtgYBj75MqQ== +jspdf@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/jspdf/-/jspdf-4.2.0.tgz#f5b42a8e1592c3da1531d005adc87ccc19272965" + integrity sha512-hR/hnRevAXXlrjeqU5oahOE+Ln9ORJUB5brLHHqH67A+RBQZuFr5GkbI9XQI8OUFSEezKegsi45QRpc4bGj75Q== dependencies: - "@babel/runtime" "^7.28.4" + "@babel/runtime" "^7.28.6" fast-png "^6.2.0" fflate "^0.8.1" optionalDependencies: canvg "^3.0.11" core-js "^3.6.0" - dompurify "^3.2.4" + dompurify "^3.3.1" html2canvas "^1.0.0-rc.5" jstimezonedetect@^1.0.7: @@ -1035,10 +1050,10 @@ signature_pad@^4.1.4: resolved "https://registry.npmjs.org/signature_pad/-/signature_pad-4.1.6.tgz" integrity sha512-eoZB8qFPfCs7o00weajp5roNnE2gY2kTNjZsh805L8V+lYPagxoZi9qrBFS3A6sgbVq++ukdzgruK7tuv3JFXQ== -sortablejs@~1.15.6: - version "1.15.6" - resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz" - integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A== +sortablejs@~1.15.7: + version "1.15.7" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.7.tgz#83a0bddc472117ee328dea20b2e6f490fed20f86" + integrity sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A== source-map@^0.6.1: version "0.6.1" From cab3f328ea669717ad672658a3efa4fad67b722c Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:46:36 -0800 Subject: [PATCH 14/57] AB#31822 - Fix parameter name typo --- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index ddbad3bb9..aadf9e732 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -188,7 +188,7 @@ if ($.fn.dataTable !== undefined && $.fn.dataTable.Api) { * @param {Function} [options.onStateSaveParams] - Hook for additional state save parameters * @param {Function} [options.onStateLoadParams] - Hook for additional state load parameters * @param {Function} [options.onStateLoaded] - Hook called after state is loaded - * @param {boolean} [options.fixedHeader=false] - Enable fixed header with scrollable body + * @param {boolean} [options.fixedHeaders=false] - Enable fixed header with scrollable body * @param {string} [options.fixedHeaderOffset='calc(100vh - 325px)'] - CSS height for fixed header offset * @returns {DataTable} Initialized DataTable API instance * @@ -224,7 +224,7 @@ function initializeDataTable(options) { onStateSaveParams, //External hooks for save/load/loaded onStateLoadParams, onStateLoaded, - fixedHeader = false, + fixedHeaders = false, fixedHeaderOffset = `calc(100vh - 325px)` } = options; @@ -392,7 +392,7 @@ function initializeDataTable(options) { }, }; - if (fixedHeader) { + if (fixedHeaders) { configuration.scrollY = fixedHeaderOffset; } From d57080f67f5dcffd2f956b006d9263643265c8fd Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:52:09 -0800 Subject: [PATCH 15/57] AB#31822 - Fix paramater naming for Scroll Y calculation --- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index aadf9e732..947cdf837 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -189,7 +189,7 @@ if ($.fn.dataTable !== undefined && $.fn.dataTable.Api) { * @param {Function} [options.onStateLoadParams] - Hook for additional state load parameters * @param {Function} [options.onStateLoaded] - Hook called after state is loaded * @param {boolean} [options.fixedHeaders=false] - Enable fixed header with scrollable body - * @param {string} [options.fixedHeaderOffset='calc(100vh - 325px)'] - CSS height for fixed header offset + * @param {string} [options.fixedHeaderScrollY='calc(100vh - 325px)'] - CSS height for the scrollable table body (DataTables scrollY) * @returns {DataTable} Initialized DataTable API instance * * @example @@ -393,7 +393,7 @@ function initializeDataTable(options) { }; if (fixedHeaders) { - configuration.scrollY = fixedHeaderOffset; + configuration.scrollY = fixedHeaderScrollY; } let iDt = new DataTable(dt, configuration); From d8d4877d9bb257df09fb8947e097ef130d085f0b Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Tue, 3 Mar 2026 17:54:02 -0800 Subject: [PATCH 16/57] AB#31871: Feature Flag Filtering AB#32021: Permission Check --- .../Assessments/AssessmentAppService.cs | 9 +++++++++ .../Repositories/AssessmentRepository.cs | 11 ++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs index 4fc3b5302..b2531de44 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs @@ -14,6 +14,7 @@ using Unity.GrantManager.Applications; using Unity.GrantManager.Comments; using Unity.GrantManager.Exceptions; +using Unity.GrantManager.Permissions; using Unity.GrantManager.Workflow; using Unity.Modules.Shared; using Volo.Abp.Application.Services; @@ -88,6 +89,14 @@ public async Task GetDisplayList(Guid applicationId) { var assessments = await _assessmentRepository.GetListWithAssessorsAsync(applicationId); var assessmentList = ObjectMapper.Map, List>(assessments); + + // If AI Scoring feature is disabled, or user doesn't have permissions to view AI assessments, filter out AI assessments from the list + var aiScoringEnabled = await _featureChecker.IsEnabledAsync("Unity.AI.Scoring"); + var canViewAI = await AuthorizationService.IsGrantedAsync(GrantApplicationPermissions.AI.ScoringAssistant.Default); + assessmentList = assessmentList + .Where(a => !a.IsAiAssessment || (aiScoringEnabled && canViewAI)) + .ToList(); + bool isApplicationUsingDefaultScoresheet = true; foreach (var assessment in assessmentList) { diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Repositories/AssessmentRepository.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Repositories/AssessmentRepository.cs index bad0bf58d..bb14a784d 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Repositories/AssessmentRepository.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.EntityFrameworkCore/Repositories/AssessmentRepository.cs @@ -61,13 +61,14 @@ public async Task> GetListWithAssess EndDate = assessment.EndDate, Status = assessment.Status, IsComplete = assessment.IsComplete, - ApprovalRecommended = assessment.ApprovalRecommended, - FinancialAnalysis = assessment.FinancialAnalysis, - EconomicImpact = assessment.EconomicImpact, - InclusiveGrowth = assessment.InclusiveGrowth, + ApprovalRecommended = assessment.ApprovalRecommended, + IsAiAssessment = assessment.IsAiAssessment, + FinancialAnalysis = assessment.FinancialAnalysis, + EconomicImpact = assessment.EconomicImpact, + InclusiveGrowth = assessment.InclusiveGrowth, CleanGrowth = assessment.CleanGrowth }); return await query.ToListAsync(); - } + } } \ No newline at end of file From 5c9460390995e6fa3ca3e8e9a6aa621339610d1e Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:59:52 -0800 Subject: [PATCH 17/57] AB#31822 - Add fixed header and footer to Grant Applicants page --- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 2 +- .../src/Unity.GrantManager.Web/Pages/Applicants/Index.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 947cdf837..3a6cea095 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -225,7 +225,7 @@ function initializeDataTable(options) { onStateLoadParams, onStateLoaded, fixedHeaders = false, - fixedHeaderOffset = `calc(100vh - 325px)` + fixedHeaderScrollY = `calc(100vh - 325px)` } = options; // Process columns and visibility diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Index.js b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Index.js index 09f86ab77..0ff706509 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Index.js +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Pages/Applicants/Index.js @@ -528,6 +528,7 @@ $(function () { pagingEnabled: true, reorderEnabled: true, languageSetValues, + fixedHeaders: true, dataTableName: 'ApplicantsTable', dynamicButtonContainerId: 'dynamicButtonContainerId', // Add state handling to validate and clear corrupted states From b4e3da5222051a87600aed2a10b30af5e5a82468 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Tue, 3 Mar 2026 19:33:22 -0800 Subject: [PATCH 18/57] AB#31822 - Add plugin to support scroll resize on table layout events --- .../UnityThemeUX2GlobalScriptContributor.cs | 1 + .../wwwroot/themes/ux2/layout.css | 11 + .../wwwroot/themes/ux2/plugins/filterRow.js | 4 + .../themes/ux2/plugins/scrollResize.js | 256 ++++++++++++++++++ .../wwwroot/themes/ux2/table-utils.js | 44 +-- 5 files changed, 280 insertions(+), 36 deletions(-) create mode 100644 applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/Bundling/UnityThemeUX2GlobalScriptContributor.cs b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/Bundling/UnityThemeUX2GlobalScriptContributor.cs index 432c73904..f2472b27c 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/Bundling/UnityThemeUX2GlobalScriptContributor.cs +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/Bundling/UnityThemeUX2GlobalScriptContributor.cs @@ -47,6 +47,7 @@ public override void ConfigureBundle(BundleConfigurationContext context) context.Files.AddIfNotContains("/themes/ux2/zone-extensions.js"); context.Files.Add("/themes/ux2/layout.js"); context.Files.Add("/themes/ux2/plugins/filterRow.js"); + context.Files.Add("/themes/ux2/plugins/scrollResize.js"); context.Files.Add("/themes/ux2/plugins/colvisAlpha.js"); context.Files.Add("/themes/ux2/table-utils.js"); context.Files.Add("/js/DateUtils.js"); diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css index 9c2a56d3f..9c1593767 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css @@ -132,6 +132,11 @@ div.dt-container { overflow-x: hidden; } +div.dt-container.dt-scroll-resize { + max-height: none !important; + overflow-y: visible; +} + .dt-container .dt-scroll-head { min-height: 44px; } @@ -892,6 +897,8 @@ input.form-control.disabled:read-only, textarea.form-control.disabled:read-only, @media (max-height: 768px) { .dt-scroll-body { overflow-y: scroll !important; + } + .dt-container:not(.dt-scroll-resize) .dt-scroll-body { max-height: calc(100vh - 42vh); } } @@ -899,6 +906,8 @@ input.form-control.disabled:read-only, textarea.form-control.disabled:read-only, @media (min-height: 769px) and (max-height: 1024px) { .dt-scroll-body { overflow-y: scroll !important; + } + .dt-container:not(.dt-scroll-resize) .dt-scroll-body { max-height: calc(100vh - 32vh); } } @@ -907,6 +916,8 @@ input.form-control.disabled:read-only, textarea.form-control.disabled:read-only, @media (min-height: 1025px) { .dt-scroll-body { overflow-y: scroll !important; + } + .dt-container:not(.dt-scroll-resize) .dt-scroll-body { max-height: calc(100vh - 30vh); } } diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/filterRow.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/filterRow.js index f536bb271..cfad9c7c2 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/filterRow.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/filterRow.js @@ -267,6 +267,7 @@ // Toggle filter row visibility $popover.find('#showFilter').on('click', function () { that.dom.filterRow.toggle(); + that.s.dt.trigger('filterRow-visibility', [that.dom.filterRow.is(':visible')]); }); // Clear all filters @@ -418,6 +419,7 @@ */ show: function () { this.dom.filterRow.show(); + this.s.dt.trigger('filterRow-visibility', [true]); return this; }, @@ -427,6 +429,7 @@ */ hide: function () { this.dom.filterRow.hide(); + this.s.dt.trigger('filterRow-visibility', [false]); return this; }, @@ -436,6 +439,7 @@ */ toggle: function () { this.dom.filterRow.toggle(); + this.s.dt.trigger('filterRow-visibility', [this.dom.filterRow.is(':visible')]); return this; }, diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js new file mode 100644 index 000000000..d8a5f43e1 --- /dev/null +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js @@ -0,0 +1,256 @@ +/** + * DataTables Feature Plugin: ScrollResize + * + * Dynamically calculates and sets the DataTable scroll body height so that + * the table header, body, and pagination footer all remain visible within + * the browser viewport at 100 % magnification, regardless of the number of + * columns, rows, or the visibility of auxiliary elements such as filter rows. + * + * Instead of relying on static CSS `calc()` values, this plugin measures the + * actual position of the scroll body in the viewport and subtracts all chrome + * (navbar, action bar, column headers, pagination) to arrive at the correct + * height. It recalculates on every event that can change the layout: + * - window resize + * - DataTables draw / column-visibility / column-reorder + * - FilterRow show / hide (custom `filterRow-visibility` event) + * - ResizeObserver on the scroll-head element (column title wrapping) + * + * Inspired by the official DataTables ScrollResize plugin + * (https://github.com/DataTables/Plugins/tree/main/features/scrollResize) + * but adapted for a full-viewport layout where `body { overflow: hidden }`. + * + * @summary Dynamic scroll-body sizing for DataTables + * @requires jQuery, DataTables 2+ + * + * @example + * // Automatic initialisation via initializeDataTable(): + * initializeDataTable({ ..., fixedHeaders: true }); + * + * @example + * // Manual initialisation after DataTable creation: + * let table = $('#example').DataTable({ scrollY: '100px', scrollCollapse: true }); + * new DataTable.ScrollResize(table); + */ + +(function ($) { + 'use strict'; + + let DataTable = $.fn.dataTable; + + if (!DataTable) { + throw new Error('DataTables ScrollResize requires DataTables'); + } + + // Resolve DataTables CSS class names from the canonical registry so the + // plugin stays correct if DataTables ever renames its classes. + let classes = DataTable.ext.classes; + let scrollClasses = classes.scrolling; + let CSS_SCROLL_BODY = scrollClasses.body; // 'dt-scroll-body' + let CSS_SCROLL_HEAD = scrollClasses.header.self; // 'dt-scroll-head' + let CSS_SCROLL_WRAP = scrollClasses.container; // 'dt-scroll' + let CSS_LAYOUT_ROW = classes.layout.row; // 'dt-layout-row' + + // Custom marker class applied by this plugin (not from DataTables) + let CSS_SCROLL_RESIZE = 'dt-scroll-resize'; + + // ----------------------------------------------------------------------- + // Constructor + // ----------------------------------------------------------------------- + + /** + * @param {DataTable.Api} dt - DataTables API instance + * @param {Object} opts - Configuration options + * @param {number} [opts.minHeight=150] Minimum scroll body height in px + * @param {number} [opts.buffer=16] Extra px subtracted as safety margin + * @param {number} [opts.throttleDelay=60] Throttle interval for resize (ms) + */ + let ScrollResize = function (dt, opts) { + if (!(this instanceof ScrollResize)) { + throw new Error("ScrollResize must be initialised with the 'new' keyword."); + } + + let table = dt.table(); + let container = $(table.container()); + + this.s = $.extend({ + minHeight: 150, + buffer: 75, + throttleDelay: 60, + }, opts); + + this.s.dt = dt; + this.s.table = $(table.node()); + this.s.container = container; + this.s.scrollBody = container.find('div.' + CSS_SCROLL_BODY); + this.s.scrollHead = container.find('div.' + CSS_SCROLL_HEAD); + this.s.namespace = '.dtScrollResize' + (ScrollResize._counter++); + + // Guard: scrollY must be enabled for a scroll body to exist + if (!this.s.scrollBody.length) { + console.warn('ScrollResize: no .' + CSS_SCROLL_BODY + ' found – is scrollY enabled?'); + return; + } + + // Mark container so CSS can opt out of static max-height rules + container.addClass(CSS_SCROLL_RESIZE); + + this._bindEvents(); + // Use a small delay so the table is fully laid out before the first calc + let that = this; + setTimeout(function () { that._size(); }, 0); + }; + + ScrollResize._counter = 0; + + // ----------------------------------------------------------------------- + // Prototype + // ----------------------------------------------------------------------- + + ScrollResize.prototype = { + + /** + * Core sizing calculation. + * + * Uses getBoundingClientRect so we automatically account for every + * element above the scroll body (navbar, action bar, search row, + * column headers, filter row, …) without hard-coding selectors. + */ + _size: function () { + let scrollBody = this.s.scrollBody; + if (!scrollBody.length || !scrollBody.is(':visible')) return; + + // 1. Where does the scroll body start in the viewport? + let scrollBodyRect = scrollBody[0].getBoundingClientRect(); + let topOffset = scrollBodyRect.top; + + // 2. How tall is the footer area below the scroll body? + let footerHeight = this._getFooterHeight(); + + // 3. Available height = viewport – top – footer – buffer + let available = window.innerHeight - topOffset - footerHeight - this.s.buffer; + let newHeight = Math.max(Math.round(available), this.s.minHeight); + + // 4. Apply – only touch the DOM when the value actually changed + let currentHeight = scrollBody[0].style.height; + let newHeightPx = newHeight + 'px'; + if (currentHeight !== newHeightPx) { + scrollBody.css({ 'height': newHeightPx, 'max-height': newHeightPx }); + } + }, + + /** + * Measure the combined height of all layout rows below the scroll body + * inside the DataTables container (pagination, info, page-length, …). + * + * DataTables 2.x DOM structure: + * div.dt-container + * div.dt-layout-row ← top controls + * div.dt-layout-row ← table row + * div.dt-layout-cell + * div.dt-scroll ← scroll wrapper + * div.dt-scroll-head + * div.dt-scroll-body + * div.dt-layout-row ← bottom controls (pagination, info) + * + * We traverse from .dt-scroll up to its parent .dt-layout-row, then + * sum the outerHeight of every subsequent sibling row. + */ + _getFooterHeight: function () { + let scrollBody = this.s.scrollBody; + let total = 0; + + // Navigate: .dt-scroll-body → .dt-scroll → .dt-layout-row + let scrollWrapper = scrollBody.closest('.' + CSS_SCROLL_WRAP); + let tableLayoutRow = scrollWrapper.closest('.' + CSS_LAYOUT_ROW); + + if (tableLayoutRow.length) { + tableLayoutRow.nextAll().each(function () { + total += $(this).outerHeight(true) || 0; + }); + } + + // Fallback: position-based detection if layout-row traversal + // found nothing (e.g. non-standard layout configuration). + if (total === 0) { + let bodyBottom = scrollBody[0].getBoundingClientRect().bottom; + this.s.container.find('.' + CSS_LAYOUT_ROW).each(function () { + if (this.getBoundingClientRect().top >= bodyBottom - 5) { + total += $(this).outerHeight(true) || 0; + } + }); + } + + return total; + }, + + /** + * Bind all the events that should trigger a recalculation. + */ + _bindEvents: function () { + let that = this; + let ns = this.s.namespace; + let dt = this.s.dt; + + // --- Window resize (throttled) --- + let resizeTimer; + $(window).on('resize' + ns, function () { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(function () { that._size(); }, that.s.throttleDelay); + }); + + // --- DataTables events --- + dt.on('draw' + ns, function () { that._size(); }); + dt.on('column-visibility' + ns, function () { + // Small delay so the DOM has reflowed after column toggle + setTimeout(function () { that._size(); }, 30); + }); + dt.on('column-reorder' + ns, function () { + setTimeout(function () { that._size(); }, 30); + }); + + // --- FilterRow visibility (custom event emitted by filterRow.js) --- + dt.on('filterRow-visibility' + ns, function () { + setTimeout(function () { that._size(); }, 30); + }); + + // --- ResizeObserver on scroll-head (detects header height changes) --- + if (typeof ResizeObserver !== 'undefined' && this.s.scrollHead.length) { + this._resizeObserver = new ResizeObserver(function () { + that._size(); + }); + this._resizeObserver.observe(this.s.scrollHead[0]); + } + + // --- Cleanup on table destroy --- + dt.on('destroy' + ns, function () { + that._destroy(); + }); + }, + + /** + * Remove all bound listeners, observers, and inline styles. + */ + _destroy: function () { + let ns = this.s.namespace; + + $(window).off(ns); + this.s.dt.off(ns); + + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; + } + + this.s.scrollBody.css({ 'height': '', 'max-height': '' }); + this.s.container.removeClass(CSS_SCROLL_RESIZE); + } + }; + + // ----------------------------------------------------------------------- + // Static reference + // ----------------------------------------------------------------------- + DataTable.ScrollResize = ScrollResize; + + return DataTable.ScrollResize; + +})(jQuery); diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 3a6cea095..7bb2ad3f0 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -188,8 +188,7 @@ if ($.fn.dataTable !== undefined && $.fn.dataTable.Api) { * @param {Function} [options.onStateSaveParams] - Hook for additional state save parameters * @param {Function} [options.onStateLoadParams] - Hook for additional state load parameters * @param {Function} [options.onStateLoaded] - Hook called after state is loaded - * @param {boolean} [options.fixedHeaders=false] - Enable fixed header with scrollable body - * @param {string} [options.fixedHeaderScrollY='calc(100vh - 325px)'] - CSS height for the scrollable table body (DataTables scrollY) + * @param {boolean} [options.fixedHeaders=false] - Enable fixed header with dynamically sized scrollable body * @returns {DataTable} Initialized DataTable API instance * * @example @@ -225,7 +224,6 @@ function initializeDataTable(options) { onStateLoadParams, onStateLoaded, fixedHeaders = false, - fixedHeaderScrollY = `calc(100vh - 325px)` } = options; // Process columns and visibility @@ -393,11 +391,16 @@ function initializeDataTable(options) { }; if (fixedHeaders) { - configuration.scrollY = fixedHeaderScrollY; + configuration.scrollY = `calc(100vh - 325px)`; // Initial value – ScrollResize plugin will recalculate dynamically } let iDt = new DataTable(dt, configuration); + // Initialize ScrollResize plugin for dynamic scroll body sizing + if (fixedHeaders && DataTable.ScrollResize) { + new DataTable.ScrollResize(iDt); + } + // Initialize FilterRow plugin initializeFilterRowPlugin(iDt); @@ -629,37 +632,6 @@ function moveButtonsToContainer(iDt, updatedActionButtons, dynamicButtonContaine } } -// ============================================================================ -// ======= RESIZE SCROLL BODY ================================================= -/** - * Dynamically adjusts the DataTable scroll body height based on container size. - * Leaves room for headers, filters, and paging. - * @param {DataTable.Api} iDt - */ -function resizeDataTableScrollBody(iDt) { - if (!iDt?.table?.()?.node) return; - - const $wrapper = $(iDt.table().container()); - const $scrollBody = $wrapper.find('.dt-scroll-body'); - if (!$scrollBody.length) return; - - let reservedHeight = 0; - reservedHeight += $wrapper.find('.dt-scroll-head').outerHeight(true) || 0; - reservedHeight += $wrapper.find('.dt-top, .dataTables_length, .dataTables_filter').outerHeight(true) || 0; - reservedHeight += $wrapper.find('.dt-bottom, .dataTables_paginate, .dataTables_info').outerHeight(true) || 0; - reservedHeight += 8; // buffer - - const $container = $wrapper.closest('.dt-container, .dataTables_wrapper'); - if (!$container.length) return; - const containerHeight = $container.innerHeight(); - if (!containerHeight) return; - - const newHeight = Math.max(containerHeight - reservedHeight, 150); - $scrollBody.css({ height: newHeight + 'px', maxHeight: newHeight + 'px' }); - - try { iDt.columns.adjust(); } catch (e) { console.warn('resizeDataTableScrollBody: columns.adjust failed', e); } -} - // ============================================================================ // Other previously existing functions (init, getSelectColumn, assignColumnIndices, etc.) remain unchanged @@ -682,7 +654,7 @@ function createNumberFormatter() { */ function addDataTableFixCSS() { if (!$('#dt-column-fix-css').length) { - $('').appendTo('head'); + $('').appendTo('head'); } } From d6f3976d7ea569b127c32fdd4cc77b28ad67ca9b Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:53:19 -0800 Subject: [PATCH 19/57] AB#31822 - Refactor ScrollResize, improve DataTable layout & sizing --- .../EndpointManagement/Endpoints/Index.js | 3 +- .../wwwroot/themes/ux2/layout.css | 9 + .../themes/ux2/plugins/scrollResize.js | 155 ++++++++---------- .../wwwroot/themes/ux2/table-utils.js | 22 ++- 4 files changed, 91 insertions(+), 98 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.TenantManagement/src/Unity.TenantManagement.Web/Pages/EndpointManagement/Endpoints/Index.js b/applications/Unity.GrantManager/modules/Unity.TenantManagement/src/Unity.TenantManagement.Web/Pages/EndpointManagement/Endpoints/Index.js index d872831bc..d7743f187 100644 --- a/applications/Unity.GrantManager/modules/Unity.TenantManagement/src/Unity.TenantManagement.Web/Pages/EndpointManagement/Endpoints/Index.js +++ b/applications/Unity.GrantManager/modules/Unity.TenantManagement/src/Unity.TenantManagement.Web/Pages/EndpointManagement/Endpoints/Index.js @@ -88,7 +88,8 @@ dataTableName: 'EndpointsTable', dynamicButtonContainerId: 'dynamicButtonContainerId', useNullPlaceholder: true, - externalSearchId: 'search-endpoints' + externalSearchId: 'search-endpoints', + fixedHeaders: true }); createModal.onResult(function () { diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css index 9c1593767..78a0f3dc2 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css @@ -893,11 +893,20 @@ input.form-control.disabled:read-only, textarea.form-control.disabled:read-only, border: var(--bs-border-width) solid var(--bs-border-color); } +table.dataTable .dt-unity-footer { + width: 100%; + display: flex; + padding-bottom: 0.25rem; + align-items: center !important; + justify-content: space-between !important; +} + @media (max-height: 768px) { .dt-scroll-body { overflow-y: scroll !important; } + .dt-container:not(.dt-scroll-resize) .dt-scroll-body { max-height: calc(100vh - 42vh); } diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js index d8a5f43e1..8419882a9 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js @@ -29,7 +29,7 @@ * @example * // Manual initialisation after DataTable creation: * let table = $('#example').DataTable({ scrollY: '100px', scrollCollapse: true }); - * new DataTable.ScrollResize(table); + * DataTable.ScrollResize(table); */ (function ($) { @@ -50,63 +50,53 @@ let CSS_SCROLL_WRAP = scrollClasses.container; // 'dt-scroll' let CSS_LAYOUT_ROW = classes.layout.row; // 'dt-layout-row' - // Custom marker class applied by this plugin (not from DataTables) + // Custom classes (not from DataTables) let CSS_SCROLL_RESIZE = 'dt-scroll-resize'; - - // ----------------------------------------------------------------------- - // Constructor - // ----------------------------------------------------------------------- + let CSS_UNITY_FOOTER = 'dt-unity-footer'; /** * @param {DataTable.Api} dt - DataTables API instance * @param {Object} opts - Configuration options * @param {number} [opts.minHeight=150] Minimum scroll body height in px - * @param {number} [opts.buffer=16] Extra px subtracted as safety margin - * @param {number} [opts.throttleDelay=60] Throttle interval for resize (ms) + * @param {number} [opts.buffer=32] Extra px subtracted as safety margin + * @param {number} [opts.throttleDelay=30] Throttle interval for resize (ms) */ - let ScrollResize = function (dt, opts) { - if (!(this instanceof ScrollResize)) { - throw new Error("ScrollResize must be initialised with the 'new' keyword."); - } - - let table = dt.table(); - let container = $(table.container()); - - this.s = $.extend({ - minHeight: 150, - buffer: 75, - throttleDelay: 60, - }, opts); - - this.s.dt = dt; - this.s.table = $(table.node()); - this.s.container = container; - this.s.scrollBody = container.find('div.' + CSS_SCROLL_BODY); - this.s.scrollHead = container.find('div.' + CSS_SCROLL_HEAD); - this.s.namespace = '.dtScrollResize' + (ScrollResize._counter++); - - // Guard: scrollY must be enabled for a scroll body to exist - if (!this.s.scrollBody.length) { - console.warn('ScrollResize: no .' + CSS_SCROLL_BODY + ' found – is scrollY enabled?'); - return; - } - - // Mark container so CSS can opt out of static max-height rules - container.addClass(CSS_SCROLL_RESIZE); - - this._bindEvents(); - // Use a small delay so the table is fully laid out before the first calc - let that = this; - setTimeout(function () { that._size(); }, 0); - }; + class ScrollResize { + constructor(dt, opts) { + if (!(this instanceof ScrollResize)) { + throw new Error("ScrollResize must be initialised with the 'new' keyword."); + } - ScrollResize._counter = 0; + let table = dt.table(); + let container = $(table.container()); + + this.s = $.extend({ + minHeight: 150, + buffer: 32, + throttleDelay: 30 + }, opts); + + this.s.dt = dt; + this.s.table = $(table.node()); + this.s.container = container; + this.s.scrollBody = container.find('div.' + CSS_SCROLL_BODY); + this.s.scrollHead = container.find('div.' + CSS_SCROLL_HEAD); + this.s.namespace = '.dtScrollResize' + (ScrollResize._counter++); + + // Guard: scrollY must be enabled for a scroll body to exist + if (!this.s.scrollBody.length) { + console.warn('ScrollResize: no .' + CSS_SCROLL_BODY + ' found – is scrollY enabled?'); + return; + } - // ----------------------------------------------------------------------- - // Prototype - // ----------------------------------------------------------------------- + // Mark container so CSS can opt out of static max-height rules + container.addClass(CSS_SCROLL_RESIZE); - ScrollResize.prototype = { + this._bindEvents(); + // Use a small delay so the table is fully laid out before the first calc + let that = this; + setTimeout(function () { that._size(); }, 0); + } /** * Core sizing calculation. @@ -115,7 +105,7 @@ * element above the scroll body (navbar, action bar, search row, * column headers, filter row, …) without hard-coding selectors. */ - _size: function () { + _size() { let scrollBody = this.s.scrollBody; if (!scrollBody.length || !scrollBody.is(':visible')) return; @@ -136,31 +126,33 @@ if (currentHeight !== newHeightPx) { scrollBody.css({ 'height': newHeightPx, 'max-height': newHeightPx }); } - }, + } /** - * Measure the combined height of all layout rows below the scroll body - * inside the DataTables container (pagination, info, page-length, …). + * Measure the height of the footer area below the scroll body + * (pagination, info, page-length controls). * - * DataTables 2.x DOM structure: - * div.dt-container - * div.dt-layout-row ← top controls - * div.dt-layout-row ← table row - * div.dt-layout-cell - * div.dt-scroll ← scroll wrapper - * div.dt-scroll-head - * div.dt-scroll-body - * div.dt-layout-row ← bottom controls (pagination, info) - * - * We traverse from .dt-scroll up to its parent .dt-layout-row, then - * sum the outerHeight of every subsequent sibling row. + * Primary: looks for the .dt-unity-footer element inside the + * container and measures its parent .dt-layout-row. + * Fallback: traverses from .dt-scroll up to its parent + * .dt-layout-row and sums every subsequent sibling row. */ - _getFooterHeight: function () { - let scrollBody = this.s.scrollBody; + _getFooterHeight() { + let container = this.s.container; let total = 0; - // Navigate: .dt-scroll-body → .dt-scroll → .dt-layout-row - let scrollWrapper = scrollBody.closest('.' + CSS_SCROLL_WRAP); + // Primary: use the .dt-unity-footer marker class + let footer = container.find('.' + CSS_UNITY_FOOTER); + if (footer.length) { + // The footer element sits inside a .dt-layout-row wrapper; + // measure whichever is the outermost so margins are included. + let row = footer.closest('.' + CSS_LAYOUT_ROW); + total = (row.length ? row : footer).outerHeight(true) || 0; + return total; + } + + // Fallback: DOM traversal for non-standard layouts + let scrollWrapper = this.s.scrollBody.closest('.' + CSS_SCROLL_WRAP); let tableLayoutRow = scrollWrapper.closest('.' + CSS_LAYOUT_ROW); if (tableLayoutRow.length) { @@ -169,24 +161,13 @@ }); } - // Fallback: position-based detection if layout-row traversal - // found nothing (e.g. non-standard layout configuration). - if (total === 0) { - let bodyBottom = scrollBody[0].getBoundingClientRect().bottom; - this.s.container.find('.' + CSS_LAYOUT_ROW).each(function () { - if (this.getBoundingClientRect().top >= bodyBottom - 5) { - total += $(this).outerHeight(true) || 0; - } - }); - } - return total; - }, + } /** * Bind all the events that should trigger a recalculation. */ - _bindEvents: function () { + _bindEvents() { let that = this; let ns = this.s.namespace; let dt = this.s.dt; @@ -221,16 +202,15 @@ this._resizeObserver.observe(this.s.scrollHead[0]); } - // --- Cleanup on table destroy --- dt.on('destroy' + ns, function () { that._destroy(); }); - }, + } /** * Remove all bound listeners, observers, and inline styles. */ - _destroy: function () { + _destroy() { let ns = this.s.namespace; $(window).off(ns); @@ -244,11 +224,10 @@ this.s.scrollBody.css({ 'height': '', 'max-height': '' }); this.s.container.removeClass(CSS_SCROLL_RESIZE); } - }; + } + + ScrollResize._counter = 0; - // ----------------------------------------------------------------------- - // Static reference - // ----------------------------------------------------------------------- DataTable.ScrollResize = ScrollResize; return DataTable.ScrollResize; diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 7bb2ad3f0..85a8f3e90 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -244,6 +244,7 @@ function initializeDataTable(options) { let configuration = { serverSide: serverSideEnabled, paging: pagingEnabled, + pageLength: 25, order: defaultSortOrder, searching: true, scrollX: true, @@ -276,9 +277,12 @@ function initializeDataTable(options) { bottomStart: null, bottomEnd: null, bottom1: { - info: { text: '_START_-_END_ of _TOTAL_' }, - paging: { buttons: 3, boundaryNumbers: true, firstLast: false }, - pageLength: { menu: [10, 25, 50, 100] }, + className: 'dt-unity-footer d-md-flex col-md', + features: [{ + info: { text: '_START_-_END_ of _TOTAL_' }, + paging: { buttons: 3, boundaryNumbers: true, firstLast: false }, + pageLength: { menu: [25, 50, 75, 100] }, + }] }, }, initComplete: function () { @@ -391,16 +395,11 @@ function initializeDataTable(options) { }; if (fixedHeaders) { - configuration.scrollY = `calc(100vh - 325px)`; // Initial value – ScrollResize plugin will recalculate dynamically + configuration.scrollY = 'calc(100vh - 325px)'; // Initial value – ScrollResize plugin will recalculate dynamically } let iDt = new DataTable(dt, configuration); - // Initialize ScrollResize plugin for dynamic scroll body sizing - if (fixedHeaders && DataTable.ScrollResize) { - new DataTable.ScrollResize(iDt); - } - // Initialize FilterRow plugin initializeFilterRowPlugin(iDt); @@ -418,6 +417,11 @@ function initializeDataTable(options) { if (originalEvent.target.nodeName.toLowerCase() === 'a') e.preventDefault(); }); + // Initialize ScrollResize plugin for dynamic scroll body sizing + if (fixedHeaders && DataTable.ScrollResize) { + iDt.settings()[0]._scrollResize = new DataTable.ScrollResize(iDt); + } + return iDt; } From 9b85676a76850a5bb8eb2004175f526fc55ccaf2 Mon Sep 17 00:00:00 2001 From: JamesPasta Date: Wed, 4 Mar 2026 11:14:07 -0800 Subject: [PATCH 20/57] feature/AB#31620-SentToFSB --- .../History/PaymentHistoryAppService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs index 0be21bf6a..439afb7c0 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/History/PaymentHistoryAppService.cs @@ -37,12 +37,12 @@ public async Task> GetPaymentHistoryList(Guid? entityId) foreach (var propertyChange in entityChange.EntityChange.PropertyChanges) { - string origninalValue = CleanValue(propertyChange.OriginalValue); + string originalValue = CleanValue(propertyChange.OriginalValue); string newValue = CleanValue(propertyChange.NewValue); string displayNewValue = MapFsbToDisplayText(newValue); // Don't display history if both original and new values are empty, as it doesn't provide useful information and may clutter the history with irrelevant entries. - if (string.IsNullOrEmpty(origninalValue) && string.IsNullOrEmpty(newValue)) + if (string.IsNullOrEmpty(originalValue) && string.IsNullOrEmpty(newValue)) { continue; } @@ -52,7 +52,7 @@ public async Task> GetPaymentHistoryList(Guid? entityId) { EntityName = GetShortEntityName(entityChange.EntityChange.EntityTypeFullName), PropertyName = propertyChange.PropertyName, - OriginalValue = origninalValue, + OriginalValue = originalValue, NewValue = displayNewValue, ChangeTime = utcDateTime.ToLocalTime(), UserName = entityChange.UserName, From 5202764e7868f6ae9972dc9f93b2c0bc10c2af43 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:20:21 -0800 Subject: [PATCH 21/57] AB#31822 - Add DataTable code quality fixes --- .../wwwroot/themes/ux2/layout.css | 2 +- .../wwwroot/themes/ux2/plugins/scrollResize.js | 16 ++++++++-------- .../wwwroot/themes/ux2/table-utils.js | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css index 78a0f3dc2..6fb5270b4 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/layout.css @@ -893,7 +893,7 @@ input.form-control.disabled:read-only, textarea.form-control.disabled:read-only, border: var(--bs-border-width) solid var(--bs-border-color); } -table.dataTable .dt-unity-footer { +.dt-unity-footer { width: 100%; display: flex; padding-bottom: 0.25rem; diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js index 8419882a9..2a07340ac 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js @@ -43,16 +43,16 @@ // Resolve DataTables CSS class names from the canonical registry so the // plugin stays correct if DataTables ever renames its classes. - let classes = DataTable.ext.classes; - let scrollClasses = classes.scrolling; - let CSS_SCROLL_BODY = scrollClasses.body; // 'dt-scroll-body' - let CSS_SCROLL_HEAD = scrollClasses.header.self; // 'dt-scroll-head' - let CSS_SCROLL_WRAP = scrollClasses.container; // 'dt-scroll' - let CSS_LAYOUT_ROW = classes.layout.row; // 'dt-layout-row' + const classes = DataTable.ext.classes; + const scrollClasses = classes.scrolling; + const CSS_SCROLL_BODY = scrollClasses.body; // 'dt-scroll-body' + const CSS_SCROLL_HEAD = scrollClasses.header.self; // 'dt-scroll-head' + const CSS_SCROLL_WRAP = scrollClasses.container; // 'dt-scroll' + const CSS_LAYOUT_ROW = classes.layout.row; // 'dt-layout-row' // Custom classes (not from DataTables) - let CSS_SCROLL_RESIZE = 'dt-scroll-resize'; - let CSS_UNITY_FOOTER = 'dt-unity-footer'; + const CSS_SCROLL_RESIZE = 'dt-scroll-resize'; + const CSS_UNITY_FOOTER = 'dt-unity-footer'; /** * @param {DataTable.Api} dt - DataTables API instance diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 829dfc9e5..ee224eed2 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -670,7 +670,7 @@ function createNumberFormatter() { */ function addDataTableFixCSS() { if (!$('#dt-column-fix-css').length) { - $('').appendTo('head'); + $('').appendTo('head'); } } From 6090196315b98fb34a35d9099e965a387984ee07 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:22:23 -0800 Subject: [PATCH 22/57] AB#31822 - Remove UTF character --- .../Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js index 2a07340ac..9eca3971a 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js @@ -103,7 +103,7 @@ * * Uses getBoundingClientRect so we automatically account for every * element above the scroll body (navbar, action bar, search row, - * column headers, filter row, …) without hard-coding selectors. + * column headers, filter row, ...) without hard-coding selectors. */ _size() { let scrollBody = this.s.scrollBody; From e15ffd10f246130db9e0f58a4c76696273f0029b Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:25:50 -0800 Subject: [PATCH 23/57] AB#31822 - Include first and last pagination in footer --- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index ee224eed2..41a36406b 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -280,7 +280,7 @@ function initializeDataTable(options) { className: 'dt-unity-footer d-md-flex col-md', features: [{ info: { text: '_START_-_END_ of _TOTAL_' }, - paging: { buttons: 3, boundaryNumbers: true, firstLast: false }, + paging: { buttons: 3, boundaryNumbers: true, firstLast: true }, pageLength: { menu: [25, 50, 75, 100] }, }] }, From 740cb1140d406cfb95ba234cc85a51c689763011 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:34:46 -0800 Subject: [PATCH 24/57] AB#31822 - Update DataTable pageLength options to include 'All' --- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 41a36406b..5b7c0a28a 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -281,7 +281,7 @@ function initializeDataTable(options) { features: [{ info: { text: '_START_-_END_ of _TOTAL_' }, paging: { buttons: 3, boundaryNumbers: true, firstLast: true }, - pageLength: { menu: [25, 50, 75, 100] }, + pageLength: { menu: [[25, 50, 75, 100, -1], [25, 50, 75, 100, 'All']] }, }] }, }, From b16d19db495edeb16c17c9f540aed39f7caa9e0e Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 4 Mar 2026 11:47:31 -0800 Subject: [PATCH 25/57] AB#32008 Optimize Office text extraction memory usage and limits --- .../AI/TextExtractionService.cs | 141 ++++++++++++------ 1 file changed, 95 insertions(+), 46 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index 88080d6a3..3b6f81b42 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -68,7 +68,7 @@ public Task ExtractTextAsync(string fileName, byte[] fileContent, string { if (extension == ".docx" || normalizedContentType.Contains("officedocument.wordprocessingml")) { - rawText = ExtractTextFromWordDocx(fileContent); + rawText = ExtractTextFromWordDocx(fileName, fileContent); return Task.FromResult(NormalizeAndLimitText(rawText, fileName)); } @@ -158,48 +158,56 @@ private string ExtractTextFromPdfFile(string fileName, byte[] fileContent) } } - private string ExtractTextFromWordDocx(byte[] fileContent) + private string ExtractTextFromWordDocx(string fileName, byte[] fileContent) { try { using var stream = new MemoryStream(fileContent, writable: false); using var document = new XWPFDocument(stream); - var parts = new List(); + var builder = new StringBuilder(); foreach (var paragraphText in document.Paragraphs.Take(MaxDocxParagraphs).Select(paragraph => paragraph.ParagraphText)) { - if (!string.IsNullOrWhiteSpace(paragraphText)) + var limitReached = AppendWithLimit(builder, paragraphText, MaxExtractedTextLength, Environment.NewLine); + if (limitReached) { - parts.Add(paragraphText); + break; } } - foreach (var table in document.Tables) + if (builder.Length < MaxExtractedTextLength) { - foreach (var row in table.Rows.Take(MaxDocxTableRows)) + foreach (var table in document.Tables) { - foreach (var cell in row.GetTableCells().Take(MaxDocxTableCellsPerRow)) + foreach (var row in table.Rows.Take(MaxDocxTableRows)) { - var text = cell.GetText(); - if (!string.IsNullOrWhiteSpace(text)) + foreach (var cell in row.GetTableCells().Take(MaxDocxTableCellsPerRow)) { - parts.Add(text); + var limitReached = AppendWithLimit(builder, cell.GetText(), MaxExtractedTextLength, Environment.NewLine); + if (limitReached) + { + break; + } + } + + if (builder.Length >= MaxExtractedTextLength) + { + break; } } - } - } - var combined = string.Join(Environment.NewLine, parts); - if (combined.Length > MaxExtractedTextLength) - { - combined = combined.Substring(0, MaxExtractedTextLength); + if (builder.Length >= MaxExtractedTextLength) + { + break; + } + } } - return combined; + return builder.ToString(); } catch (Exception ex) { - _logger.LogWarning(ex, "Word (.docx) text extraction failed"); + _logger.LogWarning(ex, "Word (.docx) text extraction failed for {FileName}", fileName); return string.Empty; } } @@ -210,12 +218,17 @@ private string ExtractTextFromExcelFile(string fileName, byte[] fileContent) { using var stream = new MemoryStream(fileContent, writable: false); using var workbook = WorkbookFactory.Create(stream); - var rows = new List(); - var totalLength = 0; + var builder = new StringBuilder(); var sheetCount = Math.Min(workbook.NumberOfSheets, MaxExcelSheets); + var limitReached = false; for (var sheetIndex = 0; sheetIndex < sheetCount; sheetIndex++) { + if (limitReached || builder.Length >= MaxExtractedTextLength) + { + break; + } + var sheet = workbook.GetSheetAt(sheetIndex); if (sheet == null) { @@ -225,42 +238,38 @@ private string ExtractTextFromExcelFile(string fileName, byte[] fileContent) var processedRows = 0; foreach (IRow row in sheet) { - if (processedRows >= MaxExcelRowsPerSheet || totalLength >= MaxExtractedTextLength) + if (processedRows >= MaxExcelRowsPerSheet || builder.Length >= MaxExtractedTextLength) { break; } - var cellTexts = row.Cells - .Take(MaxExcelCellsPerRow) - .Select(GetCellText) - .Where(value => !string.IsNullOrWhiteSpace(value)) - .ToList(); + var rowHasValue = false; + foreach (var cell in row.Cells.Take(MaxExcelCellsPerRow)) + { + var value = GetCellText(cell); + if (string.IsNullOrWhiteSpace(value)) + { + continue; + } - processedRows++; + var separator = rowHasValue ? " | " : (builder.Length > 0 ? Environment.NewLine : null); + limitReached = AppendWithLimit(builder, value, MaxExtractedTextLength, separator); + rowHasValue = true; + if (limitReached) + { + break; + } + } - if (cellTexts.Count == 0) + processedRows++; + if (limitReached) { - continue; + break; } - - var rowText = string.Join(" | ", cellTexts); - rows.Add(rowText); - totalLength += rowText.Length; - } - - if (totalLength >= MaxExtractedTextLength) - { - break; } } - var combined = string.Join(Environment.NewLine, rows); - if (combined.Length > MaxExtractedTextLength) - { - combined = combined.Substring(0, MaxExtractedTextLength); - } - - return combined; + return builder.ToString(); } catch (Exception ex) { @@ -269,6 +278,46 @@ private string ExtractTextFromExcelFile(string fileName, byte[] fileContent) } } + private static bool AppendWithLimit(StringBuilder builder, string? value, int maxLength, string? separator = null) + { + if (string.IsNullOrWhiteSpace(value)) + { + return builder.Length >= maxLength; + } + + if (builder.Length >= maxLength) + { + return true; + } + + var remaining = maxLength - builder.Length; + if (remaining <= 0) + { + return true; + } + + if (!string.IsNullOrEmpty(separator) && builder.Length > 0) + { + if (separator.Length >= remaining) + { + builder.Append(separator.AsSpan(0, remaining)); + return true; + } + + builder.Append(separator); + remaining -= separator.Length; + } + + if (value.Length >= remaining) + { + builder.Append(value.AsSpan(0, remaining)); + return true; + } + + builder.Append(value); + return false; + } + private static string GetCellText(NPOI.SS.UserModel.ICell cell) { if (cell == null) From e1c0d7dfe4176e569d46767cabb8cc30b5e2bc15 Mon Sep 17 00:00:00 2001 From: Patrick <135162612+plavoie-BC@users.noreply.github.com> Date: Wed, 4 Mar 2026 12:02:35 -0800 Subject: [PATCH 26/57] AB#31822 - DataTable code quality fixes --- .../wwwroot/themes/ux2/plugins/scrollResize.js | 6 +----- .../src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js index 9eca3971a..a264e2cc0 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/plugins/scrollResize.js @@ -29,7 +29,7 @@ * @example * // Manual initialisation after DataTable creation: * let table = $('#example').DataTable({ scrollY: '100px', scrollCollapse: true }); - * DataTable.ScrollResize(table); + * new DataTable.ScrollResize(table); */ (function ($) { @@ -63,10 +63,6 @@ */ class ScrollResize { constructor(dt, opts) { - if (!(this instanceof ScrollResize)) { - throw new Error("ScrollResize must be initialised with the 'new' keyword."); - } - let table = dt.table(); let container = $(table.container()); diff --git a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js index 5b7c0a28a..60ac3c18d 100644 --- a/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js +++ b/applications/Unity.GrantManager/modules/Unity.Theme.UX2/src/Unity.Theme.UX2/wwwroot/themes/ux2/table-utils.js @@ -244,7 +244,6 @@ function initializeDataTable(options) { let configuration = { serverSide: serverSideEnabled, paging: pagingEnabled, - pageLength: 25, order: defaultSortOrder, searching: true, scrollX: true, From 88d5f917b6efc4b4f020614f72b2017eccfbbfdb Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 4 Mar 2026 12:55:50 -0800 Subject: [PATCH 27/57] AB#32007 Apply bounded append memory optimization to PDF text extraction --- .../AI/TextExtractionService.cs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs index f1c97d1cf..66c2f52de 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/AI/TextExtractionService.cs @@ -132,24 +132,14 @@ private string ExtractTextFromPdfFile(string fileName, byte[] fileContent) foreach (var pageText in document.GetPages().Select(page => page.Text)) { - if (builder.Length >= MaxExtractedTextLength) + var limitReached = AppendWithLimit(builder, pageText, MaxExtractedTextLength, Environment.NewLine); + if (limitReached) { break; } - - if (!string.IsNullOrWhiteSpace(pageText)) - { - builder.AppendLine(pageText); - } } - var text = builder.ToString(); - if (text.Length > MaxExtractedTextLength) - { - text = text.Substring(0, MaxExtractedTextLength); - } - - return text; + return builder.ToString(); } catch (Exception ex) { From 70711dd8de5b1e4d1f65f912b3bd5a9893c446ca Mon Sep 17 00:00:00 2001 From: Armin Hasanpour Date: Wed, 4 Mar 2026 13:19:19 -0800 Subject: [PATCH 28/57] Sorting: AI assessment always first in list --- .../Assessments/AssessmentAppService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs index b2531de44..b17c530bc 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Assessments/AssessmentAppService.cs @@ -82,7 +82,9 @@ public async Task> GetListAsync(Guid applicationId) { IQueryable queryableAssessments = _assessmentRepository.GetQueryableAsync().Result; var assessments = queryableAssessments.Where(c => c.ApplicationId.Equals(applicationId)).ToList(); - return await Task.FromResult>(ObjectMapper.Map, List>(assessments.OrderByDescending(s => s.CreationTime).ToList())); + return await Task.FromResult>( + ObjectMapper.Map, List>( + assessments.OrderByDescending(s => s.IsAiAssessment).ThenByDescending(s => s.CreationTime).ToList())); } public async Task GetDisplayList(Guid applicationId) @@ -95,6 +97,8 @@ public async Task GetDisplayList(Guid applicationId) var canViewAI = await AuthorizationService.IsGrantedAsync(GrantApplicationPermissions.AI.ScoringAssistant.Default); assessmentList = assessmentList .Where(a => !a.IsAiAssessment || (aiScoringEnabled && canViewAI)) + .OrderByDescending(a => a.IsAiAssessment) + .ThenByDescending(a => a.StartDate) .ToList(); bool isApplicationUsingDefaultScoresheet = true; From 88757953b1ee118eb0f792af70934b72c36fb936 Mon Sep 17 00:00:00 2001 From: aurelio-aot Date: Wed, 4 Mar 2026 15:05:09 -0800 Subject: [PATCH 29/57] AB#27126: Create Edit Red-Stop Permission --- ...GrantApplicationPermissionDefinitionProvider.cs | 6 ++++++ .../Applicants/ApplicantAppService.cs | 4 +++- .../GrantManagerApplicationAutoMapperProfile.cs | 1 + .../Localization/GrantManager/en.json | 2 ++ .../Permissions/GrantApplicationPermissions.cs | 6 ++++++ .../ApplicantOrganizationInfo/Default.cshtml | 14 +++++++++++++- 6 files changed, 31 insertions(+), 2 deletions(-) diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs index 428f8a5d6..48cebf42b 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/Permissions/GrantApplications/GrantApplicationPermissionDefinitionProvider.cs @@ -48,6 +48,12 @@ public override void Define(IPermissionDefinitionContext context) applicatPermissions.AddChild(GrantApplicationPermissions.Applicants.ViewList, L("Permission:GrantApplicationManagement.Applicants.ViewList")); applicatPermissions.AddChild(GrantApplicationPermissions.Applicants.Edit, L("Permission:GrantApplicationManagement.Applicants.Edit")); applicatPermissions.AddChild(GrantApplicationPermissions.Applicants.AssignApplicant, L("Permission:GrantApplicationManagement.Applicants.AssignApplicant")); + var applicantInfoPermissions = applicatPermissions.AddChild( + GrantApplicationPermissions.Applicants.ApplicantInfo.Default, + L("Permission:GrantApplicationManagement.Applicants.ApplicantInfo")); + applicantInfoPermissions.AddChild( + GrantApplicationPermissions.Applicants.ApplicantInfo.EditRedStop, + L("Permission:GrantApplicationManagement.Applicants.ApplicantInfo.EditRedStop")); // Assignment var assignmentPermissions = grantApplicationPermissionsGroup.AddPermission(GrantApplicationPermissions.Assignments.Default, L("Permission:GrantApplicationManagement.Assignments.Default")); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Applicants/ApplicantAppService.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Applicants/ApplicantAppService.cs index 0568af942..92c9d8de4 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Applicants/ApplicantAppService.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/Applicants/ApplicantAppService.cs @@ -17,6 +17,7 @@ using Unity.Modules.Shared; using Unity.Modules.Shared.Utils; using Unity.Payments.Domain.Suppliers; +using Unity.GrantManager.Permissions; using Unity.Payments.Integrations.Cas; using Unity.Payments.Suppliers; using Volo.Abp.DependencyInjection; @@ -195,7 +196,8 @@ public async Task PartialUpdateApplicantSummaryAsync(Guid applicantId .Distinct(StringComparer.OrdinalIgnoreCase) .ToList() ?? []; - if (modifiedSummaryFields.Contains(nameof(UpdateApplicantSummaryDto.RedStop), StringComparer.OrdinalIgnoreCase)) + if (modifiedSummaryFields.Contains(nameof(UpdateApplicantSummaryDto.RedStop), StringComparer.OrdinalIgnoreCase) + && await AuthorizationService.IsGrantedAsync(GrantApplicationPermissions.Applicants.ApplicantInfo.EditRedStop)) { applicant.RedStop = input.Data.RedStop; } diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantManagerApplicationAutoMapperProfile.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantManagerApplicationAutoMapperProfile.cs index a46909e6d..947c58760 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantManagerApplicationAutoMapperProfile.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantManagerApplicationAutoMapperProfile.cs @@ -132,6 +132,7 @@ public GrantManagerApplicationAutoMapperProfile() CreateMap() .ForMember(dest => dest.IndigenousOrgInd, opt => opt.MapFrom(src => ConvertBoolToIndigenousOrgInd(src.IndigenousOrgInd))) + .ForMember(dest => dest.RedStop, opt => opt.Ignore()) .IgnoreNullAndDefaultValues(); CreateMap() .IgnoreNullAndDefaultValues(); diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json index 667bc316f..7ddd6771e 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Localization/GrantManager/en.json @@ -131,6 +131,8 @@ "Permission:GrantApplicationManagement.Applicants.Edit": "Edit Applicant", "Permission:GrantApplicationManagement.Applicants.ViewList": "View Applicant List", "Permission:GrantApplicationManagement.Applicants.AssignApplicant": "Assign Applicant to an Application", + "Permission:GrantApplicationManagement.Applicants.ApplicantInfo": "Applicant Info", + "Permission:GrantApplicationManagement.Applicants.ApplicantInfo.EditRedStop": "Edit Red-Stop", "Permission:GrantApplicationManagement.Assignments.Default": "Assignment", "Permission:GrantApplicationManagement.Assignments.AssignInitial": "Initial Assignment", "Permission:GrantApplicationManagement.Reviews.Default": "Reviews", diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs index b46f5c310..86bd89492 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Domain.Shared/Permissions/GrantApplicationPermissions.cs @@ -38,6 +38,12 @@ public static class Applicants public const string ViewList = Default + ".ViewList"; public const string Edit = Default + Operation.Update; public const string AssignApplicant = Default + ".AssignApplicant"; + + public static class ApplicantInfo + { + public const string Default = Applicants.Default + ".ApplicantInfo"; + public const string EditRedStop = Default + ".EditRedStop"; + } } public static class AI diff --git a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantOrganizationInfo/Default.cshtml b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantOrganizationInfo/Default.cshtml index 552b70232..0a39c40fc 100644 --- a/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantOrganizationInfo/Default.cshtml +++ b/applications/Unity.GrantManager/src/Unity.GrantManager.Web/Views/Shared/Components/ApplicantOrganizationInfo/Default.cshtml @@ -1,9 +1,14 @@ +@using Unity.GrantManager.Permissions @using Unity.GrantManager.Web.Views.Shared.Components.ApplicantOrganizationInfo +@using Volo.Abp.Authorization.Permissions + +@inject IPermissionChecker PermissionChecker @model ApplicantOrganizationInfoViewModel @{ Layout = null; + bool canEditRedStop = await PermissionChecker.IsGrantedAsync(GrantApplicationPermissions.Applicants.ApplicantInfo.EditRedStop); }