Skip to content
Merged
Changes from all commits
Commits
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,22 +1,23 @@
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Text.Json;
using Unity.GrantManager.Applications;
using Volo.Abp.DependencyInjection;
using Unity.GrantManager.GrantApplications;
using Unity.GrantManager.Intakes;
using System;
using Unity.GrantManager.Intakes.Mapping;
using Unity.GrantManager.GrantApplications;
using Unity.Payments.Events;
using Volo.Abp;
using System.Collections.Generic;
using Unity.GrantManager.Integration.Orgbook;
using Newtonsoft.Json.Linq;
using System.Linq;
using Unity.Modules.Shared.Utils;
using Microsoft.Extensions.Logging;
using Unity.Payments.Domain.Suppliers;
using Unity.Payments.Events;
using Unity.Payments.Integrations.Cas;
using Microsoft.Extensions.Logging.Abstractions;
using Unity.Payments.Suppliers;
using Unity.Payments.Domain.Suppliers;
using Volo.Abp;
using Volo.Abp.DependencyInjection;

namespace Unity.GrantManager.Applicants;

Expand Down Expand Up @@ -163,16 +164,46 @@ public async Task MatchApplicantOrgNamesAsync()
[RemoteService(true)]
public async Task<int> GetNextUnityApplicantIdAsync()
{
List<Applicant> applicants = await applicantRepository.GetApplicantsWithUnityApplicantIdAsync();
// Convert UnityApplicantId to int, filter only valid numbers
var unityIds = applicants
.Where(a => int.TryParse(a.UnityApplicantId, out _)) // Ensure it's numeric
.Select(a => int.Parse(a.UnityApplicantId!))
// Finds the first available Unity Applicant ID, starting from 100000.
var applicantQuery = await applicantRepository.GetQueryableAsync();

var relevantUnityIds = await applicantQuery
.Where(a => a.UnityApplicantId != null)
.Select(a => new { UnityApplicantId = a.UnityApplicantId, ParsedId = (int?)null })
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The anonymous type includes UnityApplicantId, but that property isn’t used later; consider projecting only ParsedId or parsing inline to simplify the code.

Suggested change
.Select(a => new { UnityApplicantId = a.UnityApplicantId, ParsedId = (int?)null })
.Select(a => new { ParsedId = (int?)null })

Copilot uses AI. Check for mistakes.
.ToListAsync();

var updatedRelevantUnityIds = relevantUnityIds
.Select(item =>
{
if (int.TryParse(item.UnityApplicantId, out var parsedId) && parsedId >= 100000)
{
return new { UnityApplicantId = item.UnityApplicantId ?? string.Empty, ParsedId = (int?)parsedId };
}
return new { UnityApplicantId = item.UnityApplicantId ?? string.Empty, ParsedId = item.ParsedId };
})
.ToList();

var orderedIds = updatedRelevantUnityIds
.Where(a => a.ParsedId.HasValue)
.Select(a => a.ParsedId!.Value) // Use the null-forgiving operator (!) to assert that ParsedId is not null
.OrderBy(id => id)
.ToList();
Comment on lines +170 to 190
Copy link

Copilot AI Jun 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code materializes two lists (relevantUnityIds and updatedRelevantUnityIds) causing multiple in-memory enumerations; consider combining parsing, filtering, and ordering in one projection to reduce overhead.

Copilot uses AI. Check for mistakes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ticket originates from 27913 -

Users can click on the 'Generate' button next to the Applicant ID field. The system will then generate an ID with the following pattern:
A 6-digit numeric value starting with 000001.
Each tenant maintains its own sequential numbering
The number is sequential and it needs to check for the next available number

User can edit the generated number
When a user modifies the Auto generated Applicant ID and clicks 'Save', the system should validate the uniqueness of the entered ID. If a duplicate ID is found, the system should display the following error message:

Applicant ID already exists. Please enter a unique ID.

int nextId = unityIds.Count > 0 ? unityIds.Max() + 1 : 000001;
int candidate = 100000; // Starting ID for availability search.

return nextId;
foreach (var id in orderedIds)
{
if (id == candidate)
{
candidate++;
}
else // Gap found: candidate is the first available ID.
{
break;
}
}

return candidate;
}

[RemoteService(true)]
Expand All @@ -188,40 +219,37 @@ public async Task<Applicant> UpdateApplicantOrgMatchAsync(Applicant applicant)
{
string? orgbookLookup = string.IsNullOrEmpty(applicant.OrgNumber) ? applicant.ApplicantName : applicant.OrgNumber;
if (string.IsNullOrEmpty(orgbookLookup)) return applicant;

JObject? result = await orgBookService.GetOrgBookQueryAsync(orgbookLookup);
var orgData = result?.SelectToken("results")?.Children().FirstOrDefault();
if (orgData == null) return applicant;

// Use the built-in System.Text.Json API
using JsonDocument result = await orgBookService.GetOrgBookAutocompleteQueryAsync(orgbookLookup);
if (!result.RootElement.TryGetProperty("results", out JsonElement results) ||
results.GetArrayLength() == 0)
return applicant;
JsonElement orgData = results[0];
await UpdateApplicantOrgNumberAsync(applicant, orgData);
await UpdateApplicantNamesAsync(applicant, orgData.SelectToken("names")?.Children());
await UpdateApplicantNamesAsync(applicant, orgData.GetProperty("names").EnumerateArray());
}
catch (Exception ex)
{
Logger.LogInformation(ex, "UpdateApplicantOrgMatchAsync: Exception: {ExceptionMessage}", ex.Message);
}

return applicant;
}

private async Task UpdateApplicantOrgNumberAsync(Applicant applicant, JToken orgData)
private async Task UpdateApplicantOrgNumberAsync(Applicant applicant, JsonElement orgData)
{
var orgNumber = orgData.SelectToken("source_id");
if (applicant.OrgNumber == null && orgNumber != null)
if (orgData.TryGetProperty("source_id", out JsonElement orgNumberElement) && applicant.OrgNumber == null)
{
applicant.OrgNumber = orgNumber.ToString();
applicant.OrgNumber = orgNumberElement.GetString();
await applicantRepository.UpdateAsync(applicant);
}
}

private async Task UpdateApplicantNamesAsync(Applicant applicant, IEnumerable<JToken>? namesChildren)
private async Task UpdateApplicantNamesAsync(Applicant applicant, IEnumerable<JsonElement> namesChildren)
{
if (namesChildren == null) return;

foreach (var name in namesChildren)
{
string nameType = name.SelectToken("type")?.ToString() ?? string.Empty;
string nameText = name.SelectToken("text")?.ToString() ?? string.Empty;
string nameType = name.TryGetProperty("type", out JsonElement typeEl) ? typeEl.GetString() ?? string.Empty : string.Empty;
string nameText = name.TryGetProperty("text", out JsonElement textEl) ? textEl.GetString() ?? string.Empty : string.Empty;

if (nameType == "entity_name")
{
Expand Down