Skip to content
Merged

Dev #2764

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e4f9feb
AB#33825 - Updated FontAwesome
hasanpour Jul 31, 2026
b1e9ccb
AB#33787 - Replaced ABP Toastr with ABP Notify
hasanpour Jul 31, 2026
ab4e6fd
AB#33825 - Updated TinyMCE
hasanpour Jul 31, 2026
b50beea
AB#33825 - Updated DataTables packages
hasanpour Jul 31, 2026
e54e296
AB#33825 - Removed ESLint
hasanpour Jul 31, 2026
61da972
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 1, 2026
73313dc
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 1, 2026
eda6b6d
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 1, 2026
e59d061
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 1, 2026
1d4c63b
AB#33825 - Removed Bootstrap 4 and migrate to 5
hasanpour Aug 1, 2026
19947bf
Merge branch 'dev' into feature/AB#33825-Upgrade-Frontend-Packages
hasanpour Aug 1, 2026
d7405df
AB#33825 - Fixed Sonar issues
hasanpour Aug 1, 2026
439701f
feature/AB#32293-DBMigratorUpdates-FixScriban
JamesPasta Aug 4, 2026
a3cc195
Merge pull request #2758 from bcgov/feature/AB#32293-DBMigratorReview
JamesPasta Aug 4, 2026
e1b356e
Merge pull request #2757 from bcgov/feature/AB#33825-Upgrade-Frontend…
JamesPasta Aug 4, 2026
4047e24
feature/AB#32293-DBMigratorUpdates-fixmapperlyOnPayments
JamesPasta Aug 4, 2026
6ff3229
Merge pull request #2759 from bcgov/feature/AB#32293-DBMigratorReview
JamesPasta Aug 4, 2026
73ed3d4
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 4, 2026
c69e716
feature/AB#32293-DBMigratorUpdates
JamesPasta Aug 4, 2026
292dde1
bugfix/AB#33901-SavedStates
JamesPasta Aug 4, 2026
1df67fb
Merge pull request #2762 from bcgov/bugfix/AB#33901-SAVED-STATES-BTN
JamesPasta Aug 4, 2026
a0d980e
Merge pull request #2763 from bcgov/feature/AB#32293-DBMigratorReview
JamesPasta Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
@page
@model Unity.AI.Web.Pages.AIReporting.IndexModel

@section styles {
@if (Model.CanViewAiReporting)
{
<abp-style src="/libs/bootstrap/css/bootstrap.css" />
}
}

@section scripts {
@if (Model.CanViewAiReporting)
{
<abp-script src="/libs/jquery/jquery.js" />
<abp-script src="/libs/bootstrap-4/dist/js/bootstrap.min.js"></abp-script>
<script>
globalThis.reportingAiUrl = @Json.Serialize(Model.ReportingAiUrl);
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public async Task<List<PaymentRequest>> GetPagedPaymentRequestsWithIncludesAsync
if (includeTags)
{
paymentsQueryable = paymentsQueryable
.Include(pr => pr.PaymentTags)
.Include(pr => pr.PaymentTags!)
.ThenInclude(pt => pt.Tag);
}

Expand All @@ -161,9 +161,7 @@ public async Task<List<PaymentRequest>> GetPagedPaymentRequestsWithIncludesAsync
paymentsQueryable = paymentsQueryable.Include(pr => pr.ExpenseApprovals);
}

#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var paymentWithIncludes = await paymentsQueryable.ToListAsync();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

return paymentWithIncludes;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ public partial class PaymentRequestToPaymentRequestDtoMapper : MapperBase<Paymen
[MapperIgnoreTarget(nameof(PaymentRequestDto.ErrorSummary))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.AccountCodingDisplay))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.CreatorUser))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.ApplicantId))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.Category))]
public override partial PaymentRequestDto Map(PaymentRequest source);

[MapperIgnoreTarget(nameof(PaymentRequestDto.ErrorSummary))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.AccountCodingDisplay))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.CreatorUser))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.ApplicantId))]
[MapperIgnoreTarget(nameof(PaymentRequestDto.Category))]
public override partial void Map(PaymentRequest source, PaymentRequestDto destination);

[MapperIgnoreTarget(nameof(ExpenseApprovalDto.DecisionUser))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ $(function () {
const formatter = createNumberFormatter();

const l = abp.localization.getResource('GrantManager');
toastr.options.positionClass = 'toast-top-center';


const UIElements = {
accountCodingDT: $('#AccountCodesDataTable'),
Expand Down Expand Up @@ -391,12 +389,12 @@ $(function () {
function updatePaymentPrefix() {
unity.payments.paymentConfigurations.paymentConfiguration.updatePaymentPrefix(UIElements.paymentPrefixInput.val())
.done(function () {
toastr.success('Payment prefix updated successfully.');
abp.notify.success('Payment prefix updated successfully.');
$('#payment-id-prefix-original').val(UIElements.paymentPrefixInput.val());
checkEnableDiscard();
})
.fail(function () {
toastr.error('Failed to update payment prefix.');
abp.notify.error('Failed to update payment prefix.');
});
};

Expand All @@ -412,7 +410,7 @@ $(function () {

function discardPaymentPrefix() {
UIElements.paymentPrefixInput.val(UIElements.originalPaymentPrefix.val());
toastr.info('Payment prefix changes discarded.');
abp.notify.info('Payment prefix changes discarded.');
checkEnableDiscard();
};

Expand All @@ -427,10 +425,10 @@ function clearFilter() {
function handleDefaultAccountCodeRadioClick(id) {
$('#AccountCodingId').val(id); // Update the hidden input with the selected account code ID
unity.payments.paymentConfigurations.paymentConfiguration.setDefaultAccountCode(id).done(function () {
toastr.success('Successfully set default account code. Reloading account codes.');
clearAccountCodesSearchAndReload();
abp.notify.success('Successfully set default account code. Reloading account codes.');
clearAccountCodesSearchAndReload();
}).fail(function () {
toastr.error('Failed to set default account code.');
abp.notify.error('Failed to set default account code.');
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,14 +543,9 @@ $(function () {
// Initialize savedStates button styling
$('.grp-savedStates').closest('.btn-group').addClass('cstm-save-view');

// Update button text based on whether any named saved views exist.
// Driven by the StateRestore extension's own 'stateRestore-change' event (the same
// event it uses internally to update its default label) rather than a localStorage
// key guess or a one-shot draw handler - those raced against the extension's own
// loading of previously-saved states from storage on page refresh.
// Update button text to Save View
function updateSavedStatesButtonText() {
const savedStatesExist = dataTable.stateRestore.states().length > 0;
$('.grp-savedStates').text(savedStatesExist ? 'Saved States' : 'Save View');
$('.grp-savedStates').text('Save View');
}

dataTable.on('stateRestore-change', updateSavedStatesButtonText);
Expand Down
2 changes: 1 addition & 1 deletion applications/Unity.GrantManager/sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ sonar.tests=test
sonar.qualitygate.wait=true

# SonarQube Exclusions (generic build artifacts and generated code only)
sonar.exclusions=src/Unity.GrantManager.EntityFrameworkCore/Migrations/**,src/Unity.GrantManager.EntityFrameworkCore/Scripts/**,**/bin/**,**/obj/**,**/wwwroot/lib/**,**/*.Designer.cs,**/node_modules/**
sonar.exclusions=src/Unity.GrantManager.EntityFrameworkCore/Migrations/**,src/Unity.GrantManager.EntityFrameworkCore/Scripts/**,**/bin/**,**/obj/**,**/wwwroot/libs/**,**/*.Designer.cs,**/node_modules/**

# Rule exclusion: javascript:S7721 "Functions should be moved to the highest possible scope"
# Disabled project-wide for JS. ABP renders pages by composing many independent widgets,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,10 @@ WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["NuGet.Config", "."]
COPY ["src/Unity.GrantManager.DbMigrator/Unity.GrantManager.DbMigrator.csproj", "src/Unity.GrantManager.DbMigrator/"]
COPY ["src/Unity.GrantManager.Application.Contracts/Unity.GrantManager.Application.Contracts.csproj", "src/Unity.GrantManager.Application.Contracts/"]
COPY ["src/Unity.GrantManager.Domain.Shared/Unity.GrantManager.Domain.Shared.csproj", "src/Unity.GrantManager.Domain.Shared/"]
COPY ["src/Unity.GrantManager.EntityFrameworkCore/Unity.GrantManager.EntityFrameworkCore.csproj", "src/Unity.GrantManager.EntityFrameworkCore/"]
COPY ["src/Unity.GrantManager.Domain/Unity.GrantManager.Domain.csproj", "src/Unity.GrantManager.Domain/"]
RUN dotnet restore "src/Unity.GrantManager.DbMigrator/Unity.GrantManager.DbMigrator.csproj"
COPY . .
RUN dotnet restore "src/Unity.GrantManager.DbMigrator/Unity.GrantManager.DbMigrator.csproj"
WORKDIR "/src/src/Unity.GrantManager.DbMigrator"
RUN dotnet build "Unity.GrantManager.DbMigrator.csproj" -c Release -o /app/build
RUN dotnet build "Unity.GrantManager.DbMigrator.csproj" -c Release -o /app/build --no-restore

FROM build AS publish
RUN dotnet publish "Unity.GrantManager.DbMigrator.csproj" -c Release -o /app/publish /p:UseAppHost=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ Because the file is optional, its absence is not an error — omit it entirely i

## Running the Migrator

The DbMigrator enables EF Core command logging by default so the migration plan,
SQL command execution, and final migration state are visible in container logs.
Sensitive data logging is not supported by the DbMigrator.

Once you've configured your connection strings via `appsettings.secrets.json` (or environment variables), you can run the migrator:

```bash
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -31,15 +30,16 @@ public GrantManagerDbMigrationService(
IEnumerable<IGrantManagerDbSchemaMigrator> dbSchemaMigrators,
ITenantRepository tenantRepository,
ICurrentTenant currentTenant,
TenantConnectionStringEncryptionMigrator connectionStringEncryptionMigrator)
TenantConnectionStringEncryptionMigrator connectionStringEncryptionMigrator,
ILogger<GrantManagerDbMigrationService> logger)
{
_dataSeeder = dataSeeder;
_dbSchemaMigrators = dbSchemaMigrators;
_tenantRepository = tenantRepository;
_currentTenant = currentTenant;
_connectionStringEncryptionMigrator = connectionStringEncryptionMigrator;

Logger = NullLogger<GrantManagerDbMigrationService>.Instance;
Logger = logger;
}

public async Task MigrateAsync()
Expand All @@ -59,6 +59,7 @@ public async Task MigrateAsync()
await _connectionStringEncryptionMigrator.MigrateAsync();

var tenants = await _tenantRepository.GetListAsync(includeDetails: true);
Logger.LogInformation("Found {TenantCount} tenants to process.", tenants.Count);

var migratedDatabaseSchemas = new HashSet<string>();

Expand All @@ -76,6 +77,11 @@ public async Task MigrateAndSeedTenantAsync(HashSet<string> migratedDatabaseSche
{
if (tenant == null) { return; }

Logger.LogInformation(
"Processing tenant {TenantName} with {ConnectionStringCount} configured connection strings.",
tenant.Name,
tenant.ConnectionStrings.Count);

using (_currentTenant.Change(tenant.Id))
{
if (tenant.ConnectionStrings.Count > 0)
Expand All @@ -94,6 +100,11 @@ public async Task MigrateAndSeedTenantAsync(HashSet<string> migratedDatabaseSche

await SeedDataAsync(tenant);
}

if (tenant.ConnectionStrings.Count == 0)
{
Logger.LogWarning("Skipping schema migration for tenant {TenantName}: no connection string is configured.", tenant.Name);
}
}

private async Task MigrateDatabaseSchemaAsync(Tenant? tenant = null)
Expand Down Expand Up @@ -121,6 +132,7 @@ private async Task SeedDataAsync(Tenant? tenant = null)
catch (Exception ex)
{
Logger.LogError(ex, "An error occurred while seeding {Database} database data.", tenant == null ? "host" : tenant.Name + TenantSuffix);
throw;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;

namespace Unity.GrantManager;

public class GrantManagerDataSeederContributor(
IApplicationStatusRepository applicationStatusRepository,
IPersonRepository personRepository,
IIdentityUserRepository userRepository,
ICurrentTenant currentTenant) : IDataSeedContributor, ITransientDependency
ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager) : IDataSeedContributor, ITransientDependency
{
public static class GrantApplicationStates
{
Expand Down Expand Up @@ -79,7 +81,9 @@ private async Task SeedApplicationStatusAsync()
{
try
{
using var unitOfWork = unitOfWorkManager.Begin(requiresNew: true, isTransactional: true);
await applicationStatusRepository.InsertAsync(status, autoSave: true);
await unitOfWork.CompleteAsync();
}
catch (Exception ex) when (IsDuplicateStatusCodeException(ex))
{
Expand All @@ -100,15 +104,24 @@ 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
try
{
using var unitOfWork = unitOfWorkManager.Begin(requiresNew: true, isTransactional: true);
await personRepository.InsertAsync(new Person
{
Id = AIScoringConstants.AiPersonId,
OidcSub = AIScoringConstants.AiOidcSub,
OidcDisplayName = AIScoringConstants.AiDisplayName,
FullName = AIScoringConstants.AiDisplayName,
Badge = AIScoringConstants.AiBadge,
TenantId = tenantId
}, autoSave: true);
await unitOfWork.CompleteAsync();
}
catch (Exception ex) when (ex.ToString().Contains("PK_Persons"))
{
Id = AIScoringConstants.AiPersonId,
OidcSub = AIScoringConstants.AiOidcSub,
OidcDisplayName = AIScoringConstants.AiDisplayName,
FullName = AIScoringConstants.AiDisplayName,
Badge = AIScoringConstants.AiBadge,
TenantId = tenantId
});
// Another concurrent seeder instance inserted the person first; safe to ignore.
}
}
}

Expand Down
Loading
Loading