From cb1d55536937b91ccc543713988a363e2f54ed8f Mon Sep 17 00:00:00 2001 From: CIS Guru Date: Thu, 29 Jan 2026 10:34:12 -0600 Subject: [PATCH] v1.0.1: Invoice processing fixes, AppImage naming, notification improvements, payment auto-apply late fees --- .../Services/PaymentService.cs | 65 +++++- .../Services/ScheduledTaskService.cs | 114 +++++----- .../Notifications/NotificationBell.razor | 58 ++--- .../NotificationPreferences.razor | 203 ++++-------------- .../Settings/Pages/ApplicationSettings.razor | 107 +++------ .../Shared/Layout/NavMenu.razor | 5 - 4-Aquiis.SimpleStart/appsettings.json | 2 +- 4-Aquiis.SimpleStart/electron.manifest.json | 10 +- .../Settings/Pages/ServiceSettings.razor | 132 +++++------- .../Shared/Layout/NavMenu.razor | 10 - 10 files changed, 272 insertions(+), 434 deletions(-) diff --git a/2-Aquiis.Application/Services/PaymentService.cs b/2-Aquiis.Application/Services/PaymentService.cs index 11993e4..2727def 100644 --- a/2-Aquiis.Application/Services/PaymentService.cs +++ b/2-Aquiis.Application/Services/PaymentService.cs @@ -402,6 +402,7 @@ public async Task GeneratePaymentNumberAsync() /// /// Updates the invoice status and paid amount after a payment change. + /// Also applies late fees if invoice becomes overdue and fees haven't been applied yet. /// private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) { @@ -420,9 +421,12 @@ private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) invoice.AmountPaid = totalPaid; - // Total due is the invoice amount (which includes late fees already added by ScheduledTaskService) + // Total due is the invoice amount (which includes late fees if already applied) var totalDue = invoice.Amount; + var previousStatus = invoice.Status; + var statusChangedToOverdue = false; + // Update invoice status based on payment // Don't change status if invoice is Cancelled or Voided if (invoice.Status != ApplicationConstants.InvoiceStatuses.Cancelled @@ -442,6 +446,7 @@ private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) if (invoice.DueOn < DateTime.Today) { invoice.Status = ApplicationConstants.InvoiceStatuses.Overdue; + statusChangedToOverdue = (previousStatus != ApplicationConstants.InvoiceStatuses.Overdue); } else { @@ -454,6 +459,7 @@ private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) if (invoice.DueOn < DateTime.Today) { invoice.Status = ApplicationConstants.InvoiceStatuses.Overdue; + statusChangedToOverdue = (previousStatus != ApplicationConstants.InvoiceStatuses.Overdue); } else { @@ -462,6 +468,13 @@ private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) } } + // If invoice just became overdue, check if late fee should be applied + if (statusChangedToOverdue && + (invoice.LateFeeApplied == null || !invoice.LateFeeApplied.Value)) + { + await ApplyLateFeeIfEligibleAsync(invoice, organizationId); + } + var userId = await _userContext.GetUserIdAsync(); invoice.LastModifiedBy = userId ?? "system"; invoice.LastModifiedOn = DateTime.UtcNow; @@ -475,5 +488,55 @@ private async Task UpdateInvoiceAfterPaymentChangeAsync(Guid invoiceId) throw; } } + + /// + /// Applies late fee to an invoice if eligible based on organization settings. + /// Uses the same logic as the scheduled task to ensure consistency. + /// + private async Task ApplyLateFeeIfEligibleAsync(Invoice invoice, Guid? organizationId) + { + try + { + // Get organization settings + var settings = await _context.OrganizationSettings + .FirstOrDefaultAsync(s => s.OrganizationId == organizationId); + + if (settings == null || !settings.LateFeeEnabled || !settings.LateFeeAutoApply) + { + // Late fees not enabled or not set to auto-apply + return; + } + + var today = DateTime.Today; + var gracePeriodCutoff = today.AddDays(-settings.LateFeeGracePeriodDays); + + // Check if invoice is past grace period + if (invoice.DueOn >= gracePeriodCutoff) + { + // Still within grace period + return; + } + + // Calculate and apply late fee + var lateFee = Math.Min(invoice.Amount * settings.LateFeePercentage, settings.MaxLateFeeAmount); + + invoice.LateFeeAmount = lateFee; + invoice.LateFeeApplied = true; + invoice.LateFeeAppliedOn = DateTime.UtcNow; + invoice.Amount += lateFee; + invoice.Notes = string.IsNullOrEmpty(invoice.Notes) + ? $"Late fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}" + : $"{invoice.Notes}\nLate fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}"; + + _logger.LogInformation( + "Late fee of {LateFee:C} auto-applied to invoice {InvoiceNumber} (ID: {InvoiceId}) during payment processing", + lateFee, invoice.InvoiceNumber, invoice.Id); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error applying late fee to invoice {InvoiceId}", invoice.Id); + // Don't throw - we don't want to fail the payment processing if late fee fails + } + } } } diff --git a/2-Aquiis.Application/Services/ScheduledTaskService.cs b/2-Aquiis.Application/Services/ScheduledTaskService.cs index 73c4d7e..c221642 100644 --- a/2-Aquiis.Application/Services/ScheduledTaskService.cs +++ b/2-Aquiis.Application/Services/ScheduledTaskService.cs @@ -117,25 +117,19 @@ private async Task DoWork(CancellationToken stoppingToken) continue; } - // Task 1: Apply late fees to overdue invoices (if enabled) - if (settings.LateFeeEnabled && settings.LateFeeAutoApply) - { - await ApplyLateFees(dbContext, organizationId, settings, stoppingToken); - } - - // Task 2: Update invoice statuses - await UpdateInvoiceStatuses(dbContext, organizationId, stoppingToken); + // Task 1: Process overdue invoices (status update + late fees) + await ProcessOverdueInvoices(dbContext, organizationId, settings, stoppingToken); - // Task 3: Send payment reminders (if enabled) + // Task 2: Send payment reminders (if enabled) if (settings.PaymentReminderEnabled) { await SendPaymentReminders(dbContext, organizationId, settings, stoppingToken); } - // Task 4: Check for expiring leases and send renewal notifications + // Task 3: Check for expiring leases and send renewal notifications await leaseNotificationService.SendLeaseRenewalRemindersAsync(organizationId, stoppingToken); - // Task 5: Expire overdue leases using workflow service (with audit logging) + // Task 4: Expire overdue leases using workflow service (with audit logging) var expiredLeaseCount = await ExpireOverdueLeases(scope, organizationId); if (expiredLeaseCount > 0) { @@ -152,7 +146,11 @@ private async Task DoWork(CancellationToken stoppingToken) } } - private async Task ApplyLateFees( + /// + /// Process overdue invoices: Update status to Overdue and apply late fees in one atomic operation. + /// This prevents the race condition where status is updated but late fees are not applied. + /// + private async Task ProcessOverdueInvoices( ApplicationDbContext dbContext, Guid organizationId, OrganizationSettings settings, @@ -161,82 +159,66 @@ private async Task ApplyLateFees( try { var today = DateTime.Today; + var gracePeriodCutoff = today.AddDays(-settings.LateFeeGracePeriodDays); - // Find overdue invoices that haven't been charged a late fee yet + // Find ALL pending invoices that are past due var overdueInvoices = await dbContext.Invoices .Include(i => i.Lease) .Where(i => !i.IsDeleted && i.OrganizationId == organizationId && i.Status == "Pending" && - i.DueOn < today.AddDays(-settings.LateFeeGracePeriodDays) && - (i.LateFeeApplied == null || !i.LateFeeApplied.Value)) + i.DueOn < today) .ToListAsync(stoppingToken); + var statusUpdatedCount = 0; + var lateFeesAppliedCount = 0; + foreach (var invoice in overdueInvoices) { - var lateFee = Math.Min(invoice.Amount * settings.LateFeePercentage, settings.MaxLateFeeAmount); - - invoice.LateFeeAmount = lateFee; - invoice.LateFeeApplied = true; - invoice.LateFeeAppliedOn = DateTime.UtcNow; - invoice.Amount += lateFee; + // Always update status to Overdue invoice.Status = "Overdue"; invoice.LastModifiedOn = DateTime.UtcNow; - invoice.LastModifiedBy = ApplicationConstants.SystemUser.Id; // Automated task - invoice.Notes = string.IsNullOrEmpty(invoice.Notes) - ? $"Late fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}" - : $"{invoice.Notes}\nLate fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}"; - - _logger.LogInformation( - "Applied late fee of {LateFee:C} to invoice {InvoiceNumber} (ID: {InvoiceId}) for organization {OrganizationId}", - lateFee, invoice.InvoiceNumber, invoice.Id, organizationId); - } - - if (overdueInvoices.Any()) - { - await dbContext.SaveChangesAsync(stoppingToken); - _logger.LogInformation("Applied late fees to {Count} invoices for organization {OrganizationId}", - overdueInvoices.Count, organizationId); - } - } - catch (Exception ex) - { - _logger.LogError(ex, "Error applying late fees for organization {OrganizationId}", organizationId); - } - } - - private async Task UpdateInvoiceStatuses(ApplicationDbContext dbContext, Guid organizationId, CancellationToken stoppingToken) - { - try - { - var today = DateTime.Today; + invoice.LastModifiedBy = ApplicationConstants.SystemUser.Id; + statusUpdatedCount++; + + // Apply late fee if: + // 1. Late fees are enabled and auto-apply is on + // 2. Grace period has elapsed + // 3. Late fee hasn't been applied yet + if (settings.LateFeeEnabled && + settings.LateFeeAutoApply && + invoice.DueOn < gracePeriodCutoff && + (invoice.LateFeeApplied == null || !invoice.LateFeeApplied.Value)) + { + var lateFee = Math.Min(invoice.Amount * settings.LateFeePercentage, settings.MaxLateFeeAmount); + + invoice.LateFeeAmount = lateFee; + invoice.LateFeeApplied = true; + invoice.LateFeeAppliedOn = DateTime.UtcNow; + invoice.Amount += lateFee; + invoice.Notes = string.IsNullOrEmpty(invoice.Notes) + ? $"Late fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}" + : $"{invoice.Notes}\nLate fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}"; - // Update pending invoices that are now overdue (and haven't had late fees applied) - var newlyOverdueInvoices = await dbContext.Invoices - .Where(i => !i.IsDeleted && - i.OrganizationId == organizationId && - i.Status == "Pending" && - i.DueOn < today && - (i.LateFeeApplied == null || !i.LateFeeApplied.Value)) - .ToListAsync(stoppingToken); + lateFeesAppliedCount++; - foreach (var invoice in newlyOverdueInvoices) - { - invoice.Status = "Overdue"; - invoice.LastModifiedOn = DateTime.UtcNow; - invoice.LastModifiedBy = ApplicationConstants.SystemUser.Id; // Automated task + _logger.LogInformation( + "Applied late fee of {LateFee:C} to invoice {InvoiceNumber} (ID: {InvoiceId}) for organization {OrganizationId}", + lateFee, invoice.InvoiceNumber, invoice.Id, organizationId); + } } - if (newlyOverdueInvoices.Any()) + if (overdueInvoices.Any()) { await dbContext.SaveChangesAsync(stoppingToken); - _logger.LogInformation("Updated {Count} invoices to Overdue status for organization {OrganizationId}", - newlyOverdueInvoices.Count, organizationId); + _logger.LogInformation( + "Processed {TotalCount} overdue invoice(s) for organization {OrganizationId}: {StatusUpdated} status updated, {LateFeesApplied} late fees applied", + overdueInvoices.Count, organizationId, statusUpdatedCount, lateFeesAppliedCount); } } catch (Exception ex) { - _logger.LogError(ex, "Error updating invoice statuses for organization {OrganizationId}", organizationId); + _logger.LogError(ex, "Error processing overdue invoices for organization {OrganizationId}", organizationId); } } diff --git a/3-Aquiis.UI.Shared/Components/Notifications/NotificationBell.razor b/3-Aquiis.UI.Shared/Components/Notifications/NotificationBell.razor index 4a96dd0..0fcca59 100644 --- a/3-Aquiis.UI.Shared/Components/Notifications/NotificationBell.razor +++ b/3-Aquiis.UI.Shared/Components/Notifications/NotificationBell.razor @@ -9,50 +9,60 @@ } -else if (notifications.Count > 0) +else { -} else { -
- -
} diff --git a/3-Aquiis.UI.Shared/Components/Notifications/NotificationPreferences.razor b/3-Aquiis.UI.Shared/Components/Notifications/NotificationPreferences.razor index 81a5ecd..df2d0bd 100644 --- a/3-Aquiis.UI.Shared/Components/Notifications/NotificationPreferences.razor +++ b/3-Aquiis.UI.Shared/Components/Notifications/NotificationPreferences.razor @@ -56,223 +56,92 @@ -
-
+
+
Email Notifications
+ + Coming Soon +
+
+ + Email notifications are coming soon! This feature requires email service integration and will be enabled in a future release. +
- -
- - @if (preferences.EnableEmailNotifications) - { -
- - - - Notifications will be sent to this email address -
- -
- -
Email Categories
-

Choose which types of notifications you want to receive via email

- -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- }
-
-
+
+
SMS Notifications
+ + Coming Soon +
+
+ + SMS notifications are coming soon! This feature requires SMS service integration and will be enabled in a future release. +
- -
- - @if (preferences.EnableSMSNotifications) - { -
- - - - Include country code (e.g., +1 for US) -
- -
- - Note: SMS notifications are for urgent matters only to minimize costs and avoid message fatigue. -
- -
- -
SMS Categories
-

Choose which urgent notifications you want to receive via SMS

- -
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- }
-
-
+
+
Digest Preferences
+ + Coming Soon +
-

+

- Digests consolidate multiple notifications into a single summary email, helping reduce email volume. -

+ Digest notifications are coming soon! This feature requires email service integration and will be enabled in a future release. +
Daily Digest
- +
- - @if (preferences.EnableDailyDigest) - { -
- - - Time of day to receive the digest -
- }
Weekly Digest
- +
- - @if (preferences.EnableWeeklyDigest) - { -
- - - - - - - - - - - Day of the week to receive the digest -
- }
diff --git a/4-Aquiis.SimpleStart/Features/Administration/Settings/Pages/ApplicationSettings.razor b/4-Aquiis.SimpleStart/Features/Administration/Settings/Pages/ApplicationSettings.razor index 3b4d7cf..cabdb35 100644 --- a/4-Aquiis.SimpleStart/Features/Administration/Settings/Pages/ApplicationSettings.razor +++ b/4-Aquiis.SimpleStart/Features/Administration/Settings/Pages/ApplicationSettings.razor @@ -174,33 +174,11 @@
-
-
-
-
Apply Late Fees
- Process overdue invoices and apply late fees -
- -
- @if (taskResults.ContainsKey(TaskType.ApplyLateFees)) - { -
- @taskResults[TaskType.ApplyLateFees] -
- } -
-
Update Invoice Statuses
- Mark pending invoices as overdue + Mark pending invoices as overdue and apply late fees
- diff --git a/4-Aquiis.SimpleStart/appsettings.json b/4-Aquiis.SimpleStart/appsettings.json index d04f5ae..acbbea3 100644 --- a/4-Aquiis.SimpleStart/appsettings.json +++ b/4-Aquiis.SimpleStart/appsettings.json @@ -13,7 +13,7 @@ "AllowedHosts": "*", "ApplicationSettings": { "AppName": "Aquiis", - "Version": "1.0.0", + "Version": "1.0.1", "Author": "CIS Guru", "Email": "cisguru@outlook.com", "Repository": "https://github.com/xnodeoncode/Aquiis", diff --git a/4-Aquiis.SimpleStart/electron.manifest.json b/4-Aquiis.SimpleStart/electron.manifest.json index 34cd034..53e0fab 100644 --- a/4-Aquiis.SimpleStart/electron.manifest.json +++ b/4-Aquiis.SimpleStart/electron.manifest.json @@ -17,9 +17,9 @@ "aspCoreBackendPort": 8888, "build": { "appId": "com.aquiis.propertymanagement", - "productName": "AquiisPropertyManagement", + "productName": "Aquiis", "copyright": "Copyright © 2026", - "buildVersion": "1.0.0", + "buildVersion": "1.0.1", "compression": "normal", "directories": { "output": "../../../bin/Desktop" @@ -47,12 +47,14 @@ "win": { "target": "portable", "icon": "bin/Assets/icon.ico", - "signAndEditExecutable": false + "signAndEditExecutable": false, + "artifactName": "${productName}-${version}-${arch}.${ext}" }, "linux": { "target": "AppImage", "icon": "bin/Assets/icon.png", - "category": "Office" + "category": "Office", + "artifactName": "${productName}-${version}-${arch}.${ext}" } } } diff --git a/5-Aquiis.Professional/Features/Administration/Settings/Pages/ServiceSettings.razor b/5-Aquiis.Professional/Features/Administration/Settings/Pages/ServiceSettings.razor index 3d4cea7..ed97dbe 100644 --- a/5-Aquiis.Professional/Features/Administration/Settings/Pages/ServiceSettings.razor +++ b/5-Aquiis.Professional/Features/Administration/Settings/Pages/ServiceSettings.razor @@ -43,43 +43,21 @@
-
Apply Late Fees
- Process overdue invoices and apply late fees based on organization settings +
Process Overdue Invoices
+ Update invoice status to Overdue and apply late fees in one atomic operation
-
- @if (taskResults.ContainsKey(TaskType.ApplyLateFees)) + @if (taskResults.ContainsKey(TaskType.ProcessOverdueInvoices)) {
- @taskResults[TaskType.ApplyLateFees] -
- } -
- -
-
-
-
Update Invoice Statuses
- Mark pending invoices as overdue based on due dates -
- -
- @if (taskResults.ContainsKey(TaskType.UpdateInvoiceStatuses)) - { -
- @taskResults[TaskType.UpdateInvoiceStatuses] + @taskResults[TaskType.ProcessOverdueInvoices]
}
@@ -167,8 +145,7 @@
-

Apply Late Fees: Applies late fees to invoices that are past the grace period based on organization-specific settings.

-

Update Invoice Statuses: Changes invoice status from "Pending" to "Overdue" for invoices past their due date.

+

Process Overdue Invoices: Updates invoice status to "Overdue" and applies late fees (if enabled) for all invoices past their due date. This atomic operation prevents the race condition where status updates without late fees.

Send Payment Reminders: Marks invoices for payment reminders when they're approaching their due date.

Check Lease Renewals: Processes lease expiration notifications at 90, 60, and 30 days, and marks expired leases.

@@ -185,8 +162,7 @@ private enum TaskType { - ApplyLateFees, - UpdateInvoiceStatuses, + ProcessOverdueInvoices, SendPaymentReminders, CheckLeaseRenewals, All @@ -214,11 +190,8 @@ switch (taskType) { - case TaskType.ApplyLateFees: - await ApplyLateFees(organizationId.Value); - break; - case TaskType.UpdateInvoiceStatuses: - await UpdateInvoiceStatuses(organizationId.Value); + case TaskType.ProcessOverdueInvoices: + await ProcessOverdueInvoices(organizationId.Value); break; case TaskType.SendPaymentReminders: await SendPaymentReminders(organizationId.Value); @@ -258,8 +231,7 @@ return; } - await ApplyLateFees(organizationId.Value); - await UpdateInvoiceStatuses(organizationId.Value); + await ProcessOverdueInvoices(organizationId.Value); await SendPaymentReminders(organizationId.Value); await CheckLeaseRenewals(organizationId.Value); @@ -284,42 +256,64 @@ return await UserContext.GetActiveOrganizationIdAsync(); } - private async Task ApplyLateFees(Guid organizationId) + /// + /// Process overdue invoices: Update status to Overdue and apply late fees in one atomic operation. + /// This prevents the race condition where status is updated but late fees are not applied. + /// + private async Task ProcessOverdueInvoices(Guid organizationId) { var settings = await PropertyService.GetOrganizationSettingsByOrgIdAsync(organizationId); - if (settings == null || !settings.LateFeeEnabled || !settings.LateFeeAutoApply) + if (settings == null) { - var reason = settings == null ? "Settings not found" - : !settings.LateFeeEnabled ? "Late fees disabled" - : "Auto-apply disabled"; - taskResults[TaskType.ApplyLateFees] = $"No late fees applied: {reason} (OrgId: {organizationId})"; + taskResults[TaskType.ProcessOverdueInvoices] = "Settings not found"; return; } var today = DateTime.Today; + var gracePeriodCutoff = today.AddDays(-settings.LateFeeGracePeriodDays); + + // Find ALL pending invoices that are past due var overdueInvoices = await DbContext.Invoices .Include(i => i.Lease) .Where(i => !i.IsDeleted && i.OrganizationId == organizationId && i.Status == "Pending" && - i.DueOn < today.AddDays(-settings.LateFeeGracePeriodDays) && - (i.LateFeeApplied == null || !i.LateFeeApplied.Value)) + i.DueOn < today) .ToListAsync(); + var statusUpdatedCount = 0; + var lateFeesAppliedCount = 0; + foreach (var invoice in overdueInvoices) { - var lateFee = Math.Min(invoice.Amount * settings.LateFeePercentage, settings.MaxLateFeeAmount); - invoice.LateFeeAmount = lateFee; - invoice.LateFeeApplied = true; - invoice.LateFeeAppliedOn = DateTime.UtcNow; - invoice.Amount += lateFee; + // Always update status to Overdue invoice.Status = "Overdue"; invoice.LastModifiedOn = DateTime.UtcNow; invoice.LastModifiedBy = ApplicationConstants.SystemUser.Id; - invoice.Notes = string.IsNullOrEmpty(invoice.Notes) - ? $"Late fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}" - : $"{invoice.Notes}\nLate fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}"; + statusUpdatedCount++; + + // Apply late fee if: + // 1. Late fees are enabled and auto-apply is on + // 2. Grace period has elapsed + // 3. Late fee hasn't been applied yet + if (settings.LateFeeEnabled && + settings.LateFeeAutoApply && + invoice.DueOn < gracePeriodCutoff && + (invoice.LateFeeApplied == null || !invoice.LateFeeApplied.Value)) + { + var lateFee = Math.Min(invoice.Amount * settings.LateFeePercentage, settings.MaxLateFeeAmount); + + invoice.LateFeeAmount = lateFee; + invoice.LateFeeApplied = true; + invoice.LateFeeAppliedOn = DateTime.UtcNow; + invoice.Amount += lateFee; + invoice.Notes = string.IsNullOrEmpty(invoice.Notes) + ? $"Late fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}" + : $"{invoice.Notes}\nLate fee of {lateFee:C} applied on {DateTime.Now:MMM dd, yyyy}"; + + lateFeesAppliedCount++; + } } if (overdueInvoices.Any()) @@ -327,33 +321,7 @@ await DbContext.SaveChangesAsync(); } - taskResults[TaskType.ApplyLateFees] = $"Applied late fees to {overdueInvoices.Count} invoice(s)"; - } - - private async Task UpdateInvoiceStatuses(Guid organizationId) - { - var today = DateTime.Today; - var newlyOverdueInvoices = await DbContext.Invoices - .Where(i => !i.IsDeleted && - i.OrganizationId == organizationId && - i.Status == "Pending" && - i.DueOn < today && - (i.LateFeeApplied == null || !i.LateFeeApplied.Value)) - .ToListAsync(); - - foreach (var invoice in newlyOverdueInvoices) - { - invoice.Status = "Overdue"; - invoice.LastModifiedOn = DateTime.UtcNow; - invoice.LastModifiedBy = ApplicationConstants.SystemUser.Id; - } - - if (newlyOverdueInvoices.Any()) - { - await DbContext.SaveChangesAsync(); - } - - taskResults[TaskType.UpdateInvoiceStatuses] = $"Updated {newlyOverdueInvoices.Count} invoice(s) to Overdue status"; + taskResults[TaskType.ProcessOverdueInvoices] = $"Processed {overdueInvoices.Count} overdue invoice(s): {statusUpdatedCount} status updated, {lateFeesAppliedCount} late fees applied"; } private async Task SendPaymentReminders(Guid organizationId) diff --git a/5-Aquiis.Professional/Shared/Layout/NavMenu.razor b/5-Aquiis.Professional/Shared/Layout/NavMenu.razor index acfb10a..8b9b7d6 100644 --- a/5-Aquiis.Professional/Shared/Layout/NavMenu.razor +++ b/5-Aquiis.Professional/Shared/Layout/NavMenu.razor @@ -69,11 +69,6 @@
Tours
-
- -