Skip to content
Merged

Dev #1477

Show file tree
Hide file tree
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
Expand Up @@ -16,5 +16,6 @@ public interface IPaymentRequestAppService : IApplicationService
Task<List<PaymentRequestDto>> UpdateStatusAsync(List<UpdatePaymentStatusRequestDto> paymentRequests);
Task<int> GetPaymentRequestCountBySiteIdAsync(Guid siteId);
Task<List<PaymentDetailsDto>> GetListByApplicationIdsAsync(List<Guid> applicationIds);
Task<string> GetNextBatchInfoAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ public virtual async Task<List<PaymentRequestDto>> CreateAsync(List<CreatePaymen
return createdPayments;
}

public async Task<string> GetNextBatchInfoAsync()
{
var (paymentConfig, _) = await GetPaymentConfigurationWithThresholdAsync();
var paymentIdPrefix = string.Empty;

if (paymentConfig != null && !paymentConfig.PaymentIdPrefix.IsNullOrEmpty())
{
paymentIdPrefix = paymentConfig.PaymentIdPrefix;
}

var batchNumber = await GetMaxBatchNumberAsync();
var batchName = $"{paymentIdPrefix}_UNITY_BATCH_{batchNumber}";

return batchName;
}

private static string GenerateInvoiceNumberAsync(string referenceNumber, string invoiceNumber, string sequencePart)
{
return $"{referenceNumber}-{invoiceNumber}-{sequencePart}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
"ApplicationPaymentRequest:SiteNumber": "Site #",
"ApplicationPaymentRequest:SubmitButtonText": "Submit Payment Requests",
"ApplicationPaymentRequest:CancelButtonText": "Cancel",
"ApplicationPaymentRequest:BatchNumberName": "Batch #",
"ApplicationPaymentRequest:NumberPayment": "Number of Payments",
"ApplicationPaymentRequest:TotalAmount": "Total Amount",

"ApplicationPaymentRequest:Validations:RemainingAmountExceeded": "Cannot add a payment that exceeds the remaining amount of ",
"ApplicationPaymentRequest:Validations:L2ApproverRestriction": "You cannot approve this payment as you have already approved it as an L1 Approver.",
"ApplicationPaymentRequest:Validations:L2ApproverRestrictionBatch": "Highlighted payments were already approved with L1 permission. L1 and L2 approvers must be different individuals",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
</h6>
</div>
<div class="flex-shrink-1">
<abp-button onclick='removeApplicationPayment("@Model.PaymentGroupings[k].Items[i].Id" + "_container", @Model.PaymentGroupings[k].GroupId)'
<abp-button onclick='removeApplicationPaymentApproval("@Model.PaymentGroupings[k].Items[i].Id" + "_container", @Model.PaymentGroupings[k].GroupId)'
size="Small" icon-type="Other" class="m-0 p-0 remove-single-payment" icon="fa fa-times"
data-parameter="@Model.PaymentGroupings[k].Items[i].CorrelationId" />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

function removeApplicationPayment(applicationId, groupId) {
function removeApplicationPaymentApproval(applicationId, groupId) {
$('#' + applicationId).remove();
let applicationCount = $('#ApplicationCount').val();
let groupCount = $(`#${groupId}_count`).val();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,35 @@
@{
Layout = null;
}

<form method="post" asp-page-handler="OnPostAsync" id="paymentform">
<abp-modal size="ExtraLarge" id="payment-modal">
<abp-modal-header class="payment-modal-header" title="@L["ApplicationPaymentRequest:Title"].Value"></abp-modal-header>
<abp-modal-body>
<abp-card>
<abp-card class="pb-0">
<abp-card-body class="pb-0 mb-0">
<abp-row class="m-0 p-1">
<abp-column size="_4">
<abp-input asp-for="@Model.BatchNumberDisplay" type="text" label="@L["ApplicationPaymentRequest:BatchNumberName"]" disabled="true" />
</abp-column>
<abp-column size="_4">
<abp-input id="ApplicationCount" type="text" asp-for="ApplicationPaymentRequestForm.Count" label="@L["ApplicationPaymentRequest:NumberPayment"]" disabled="true" />
</abp-column>
<abp-column size="_4">
<abp-input asp-for="@Model.TotalAmount" class="totalAmount unity-currency-input" type="text" label="@L["ApplicationPaymentRequest:TotalAmount"]" disabled="true" />
</abp-column>
</abp-row>
</abp-card-body>
</abp-card>
<abp-card class="pt-0">
<abp-card-body class="payment-card">
<abp-input id="PaymentThreshold" type="hidden" asp-for="PaymentThreshold" />
<abp-input id="ApplicationCount" type="hidden" asp-for="ApplicationPaymentRequestForm.Count" />
@for (var i = 0; i < Model.ApplicationPaymentRequestForm?.Count; i++)
{
<div id="@($"{Model.ApplicationPaymentRequestForm[i].CorrelationId}_container")" class="single-payment">
<abp-row class="m-0 p-2">
<abp-column size="_11" class="px-1"><h6 class="single-payment-card-application-name">@Model.ApplicationPaymentRequestForm[i].ApplicantName/@Model.ApplicationPaymentRequestForm[i].InvoiceNumber</h6></abp-column>
<abp-column size="_1" class="px-1 remove-single-payment"> <abp-button onclick='removeApplicationPayment("@Model.ApplicationPaymentRequestForm[i].CorrelationId" + "_container")' size="Small" icon-type="Other" class="m-0 p-0 remove-single-payment" icon="fa fa-times" data-parameter="@Model.ApplicationPaymentRequestForm[i].CorrelationId" /></abp-column>
<abp-column size="_1" class="px-1 remove-single-payment"> <abp-button onclick='removeApplicationPaymentRequest("@Model.ApplicationPaymentRequestForm[i].CorrelationId" + "_container")' size="Small" icon-type="Other" class="m-0 p-0 remove-single-payment" icon="fa fa-times" data-parameter="@Model.ApplicationPaymentRequestForm[i].CorrelationId" /></abp-column>
</abp-row>

<input name="ApplicationPaymentRequestForm.Index" type="hidden" value="@i" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Unity.Payment.Shared;
using System.Text.Json;
using Unity.Payments.Domain.Suppliers;
using System.Linq;

namespace Unity.Payments.Web.Pages.Payments
{
Expand Down Expand Up @@ -46,6 +47,20 @@ public CreatePaymentRequestsModel(
[BindProperty]
public bool HasPaymentConfiguration { get; set; }

[BindProperty]
public string BatchNumberDisplay { get; set; } = string.Empty;


[BindProperty]
public decimal TotalAmount { get; set; }

public decimal ApplicationPaymentRequestFormTotalAmount
{
get
{
return ApplicationPaymentRequestForm?.Sum(x => x.Amount) ?? 0m;
}
}

public async Task OnGetAsync(string applicationIds)
{
Expand Down Expand Up @@ -105,6 +120,9 @@ public async Task OnGetAsync(string applicationIds)
ApplicationPaymentRequestForm!.Add(request);
}

var batchName = await _paymentRequestService.GetNextBatchInfoAsync();
BatchNumberDisplay = batchName;
TotalAmount = ApplicationPaymentRequestForm?.Sum(x => x.Amount) ?? 0m;
}

private static List<string> GetErrorlist(SupplierDto? supplier, Site? site, GrantApplicationDto application, decimal remainingAmount)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@

function removeApplicationPayment(applicationId) {
function removeApplicationPaymentRequest(applicationId) {
let $container = $('#' + applicationId);

// Get the amount value inside this container before removing it
let amountValue = $container.find('.amount').val();
let amount = parseFloat((amountValue || "0").replace(/,/g, ''));

// Update the total amount
let $totalInput = $('.totalAmount');
let currentTotal = parseFloat(($totalInput.val() || "0").replace(/,/g, '')) || 0;
let newTotal = currentTotal - amount;
if (newTotal < 0) newTotal = 0;
$totalInput.val(newTotal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }));

$('#' + applicationId).remove();
let applicationCount = $('#ApplicationCount').val();
$('#ApplicationCount').val(applicationCount - 1);
Expand All @@ -10,7 +23,7 @@ function removeApplicationPayment(applicationId) {
if (!$('div.single-payment').length) {
$('#no-payment-msg').css("display", "block");
$("#payment-modal").find('#btnSubmitPayment').prop("disabled", true);
}
}
else {
$('#no-payment-msg').css("display", "none");
}
Expand Down Expand Up @@ -43,6 +56,4 @@ function submitPayments() {
} else {
$('#paymentform').submit();
}
};


};
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@
abp.notify.success('The payment info has been updated.');
disableSaveButton(true);
refreshSupplierInfoWidget();
hideSpinner();
abp.ui.unblock();
})
.catch((error) => {
console.error(error);
disableSaveButton(false);
})
.finally(() => {
hideSpinner();
abp.ui.unblock();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ $(function () {
const UIElements = {
navOrgInfoTab: $('#nav-organization-info-tab'),
siteId: $("#SiteId"),
paymentApplicantId: $("#PaymentInfo_ApplicantId"),
originalSupplierNumber: $("#OriginalSupplierNumber"),
supplierNumber: $("#SupplierNumber"),
supplierName: $("#SupplierName"),
hasEditSupplier: $("#HasEditSupplierInfo"),
refreshSitesBtn: $("#btn-refresh-sites"),
orgName: $("#ApplicantInfo_OrgName"), // Note: Dependent on Applicant Info Tab
nonRegisteredOrgName: $("#ApplicantInfo_NonRegOrgName"), // Note: Dependent on Applicant Info Tab
orgName: $("#ApplicantSummary_OrgName"), // Note: Dependent on Applicant Info Tab
nonRegisteredOrgName: $("#ApplicantSummary_NonRegOrgName"), // Note: Dependent on Applicant Info Tab
supplierOrgInfoErrorDiv: $("#supplier-error-div")
};

Expand All @@ -26,23 +27,50 @@ $(function () {
init();

function validateMatchingSupplierToOrgInfo() {
const supplierName = (UIElements.supplierName.val() || '').toLowerCase().trim();
if (UIElements.paymentApplicantId.length === 0) {
console.warn('Payment Applicant ID element not found. Skipping validation.');
UIElements.supplierOrgInfoErrorDiv.toggleClass('hidden', true);
return
}

const applicantId = UIElements.paymentApplicantId.val();
let supplierName = ($("#SupplierName").val() || '').toLowerCase().trim();

if (!supplierName) {
UIElements.supplierOrgInfoErrorDiv.toggleClass('hidden', true);
return;
}

const orgName = (UIElements.orgName.val() || '').toLowerCase().trim();
const nonRegisteredOrgName = (UIElements.nonRegisteredOrgName.val() || '').toLowerCase().trim();

// Match if either orgName or nonRegisteredOrgName matches supplierName
const isMatch =
(!orgName && !nonRegisteredOrgName) ||
supplierName === orgName ||
supplierName === nonRegisteredOrgName;

UIElements.supplierOrgInfoErrorDiv.toggleClass('hidden', isMatch);
const orgNameElem = UIElements.orgName;
const nonRegOrgNameElem = UIElements.nonRegisteredOrgName;
const orgNameExists = orgNameElem.length > 0;
const nonRegOrgNameExists = nonRegOrgNameElem.length > 0;

// If neither element exists, fallback on API check
if (!orgNameExists && !nonRegOrgNameExists) {
// NOTE: External module dependency on Unity.GrantManager.GrantApplication.ApplicationApplicantAppService
unity.grantManager.grantApplications
.applicationApplicant
.getSupplierNameMatchesCheck(applicantId, supplierName)
.then((isMatch) => {
abp.notify.success(`Supplier info is now ${isMatch}`);
$("#supplier-error-div").toggleClass('hidden', isMatch);
})
.catch((error) => {
console.error(error);
});
} else {
// Only fetch values if elements exist
const orgName = orgNameExists ? (orgNameElem.val() || '').toLowerCase().trim() : '';
const nonRegisteredOrgName = nonRegOrgNameExists ? (nonRegOrgNameElem.val() || '').toLowerCase().trim() : '';

// Hides warning if there is a match
let isMatch =
(!orgName && !nonRegisteredOrgName) ||
supplierName === orgName ||
supplierName === nonRegisteredOrgName;
$("#supplier-error-div").toggleClass('hidden', isMatch);
}
}

function bindUIEvents() {
Expand Down Expand Up @@ -276,6 +304,7 @@ $(function () {
(msg, data) => {
UIElements.siteId.val(data);
loadSiteInfoTable();
validateMatchingSupplierToOrgInfo();
}
);

Expand All @@ -291,10 +320,10 @@ function saveSiteDefault(siteId) {
type: "POST",
data: JSON.stringify({ ApplicantId: applicantId, SiteId: siteId }),
})
.then(response => {
abp.notify.success('Default site has been successfully saved.', 'Default Site Saved');
})
.catch(error => {
console.error('There was a problem with the post operation:', error);
});
.then(response => {
abp.notify.success('Default site has been successfully saved.', 'Default Site Saved');
})
.catch(error => {
console.error('There was a problem with the post operation:', error);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ $(function () {
categoriesSelect.appendChild(option);
});
}
onSubmissionSummaryFilterChanged();
});

let inputAction = function (requestData, dataTableSettings) {
Expand All @@ -40,7 +41,7 @@ $(function () {
x.tenant.toLowerCase().includes($('#ReconciliationTenantFilter').val().toLowerCase()) &&
(isNaN(dateTo.getTime()) || new Date(x.createdAt) <= dateTo) &&
(isNaN(dateFrom.getTime()) || new Date(x.createdAt) >= dateFrom) &&
(x.category == $("#ReconciliationCategoryFilter").val() || $("#ReconciliationCategoryFilter").val() == null)
(x.category == $("#ReconciliationCategoryFilter").val() || $("#ReconciliationCategoryFilter").val() == "all")
);

let totalSubmissions = filtered_submissions.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<div>
<label for="ReconciliationCategoryFilter">Category:</label>
<select name="category" class="form-control input-sm custom-filter-input" id="ReconciliationCategoryFilter" onchange="onSubmissionSummaryFilterChanged()">
<option value="" selected disabled hidden>Category</option>
<option value="all" selected>All</option>
</select>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public interface IApplicationApplicantAppService : IApplicationService
Task<ApplicationApplicantInfoDto> GetByApplicationIdAsync(Guid applicationId);
Task<ApplicantInfoDto> GetApplicantInfoTabAsync(Guid applicationId);
Task<GrantApplicationDto> UpdatePartialApplicantInfoAsync(Guid applicationId, PartialUpdateDto<UpdateApplicantInfoDto> input);
Task<bool> GetSupplierNameMatchesCheck(Guid applicantId, string? supplierName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,29 @@ await localEventBus.PublishAsync(new PersistWorksheetIntanceValuesEto()
}
}
}

public async Task<bool> GetSupplierNameMatchesCheck(Guid applicantId, string? supplierName)
{
if (string.IsNullOrWhiteSpace(supplierName))
{
return true; // If supplierName is null or empty, there is nothing to warn about
}

var applicant = await applicantRepository.GetAsync(applicantId) ?? throw new EntityNotFoundException();

var normalizedSupplierName = supplierName?.Trim();
var organizationName = applicant.OrgName?.Trim();
var nonRegisteredOrganizationName = applicant.NonRegOrgName?.Trim();

// Match if either orgName or nonRegisteredOrgName matches supplierName
// - If both orgName and nonRegisteredOrgName are null or empty, return true
// - Otherwise, return true if supplierName matches either orgName or nonRegisteredOrgName (case-insensitive)
if (string.IsNullOrEmpty(organizationName) && string.IsNullOrEmpty(nonRegisteredOrganizationName))
{
return true;
}

return string.Equals(normalizedSupplierName, organizationName, StringComparison.OrdinalIgnoreCase)
|| string.Equals(normalizedSupplierName, nonRegisteredOrganizationName, StringComparison.OrdinalIgnoreCase);
}
}
Loading