Close all GL entry gaps across the accounting surface
- Stripe payments/refunds/chargebacks now post DR/CR entries (PaymentController) - Vendor credit void now reverses the posted GL lines (VendorCreditsController) - Gift certificate issue/redeem/void post GL to account 2500 GC Liability; FinancialReportService Trial Balance + Balance Sheet include GC liability and breakage income; P&L shows deferred revenue deduction and breakage income line - Customer deposits now post DR Checking / CR 2300 on record, reverse on delete; invoice auto-apply uses DR 2300 / CR AR (not a second bank debit); draft invoice delete reverses deposit-apply GL before the AR reversal - Deposit.DepositAccountId column added; account 2300 seeded via migration - InvoicesController.ApplyCredit now posts DR Sales Discounts / CR AR, consistent with CreditMemosController.Apply - IssueRefund (cash/card) posts DR AR / CR Bank and sets Refund.DepositAccountId; refund modal gains a bank account selector hidden for store-credit path - CancelRefund (cash/card) reverses the IssueRefund GL entries - LedgerService GetAccountLedgerAsync + ComputePriorBalanceAsync now include Refunds, CreditMemoApplications, VendorCreditApplications, GC Liability (2500), and Customer Deposits (2300) so account ledger view and RecalculateAllAsync produce correct balances - Three EF migrations applied: SeedSalesDiscountsAccount, AccountingGapsPhase2, AccountingDepositsGL - Unit tests updated for new IAccountBalanceService constructor params (200/200) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using PowderCoating.Application.Interfaces;
|
||||
using PowderCoating.Core.Entities;
|
||||
using PowderCoating.Core.Enums;
|
||||
using PowderCoating.Core.Interfaces;
|
||||
@@ -15,6 +16,9 @@ namespace PowderCoating.Web.Controllers;
|
||||
/// balance and can be issued standalone (goodwill, billing correction) or linked to an original
|
||||
/// invoice (price dispute, rework resolution). Applied portions reduce invoice BalanceDue and
|
||||
/// customer.CreditBalance atomically inside a transaction.
|
||||
/// GL entries on Apply: DR 4950 Sales Discounts (contra-revenue) / CR AR — mirrors the treatment
|
||||
/// of invoice discounts so the Trial Balance and Balance Sheet reflect the applied credit as both
|
||||
/// a revenue deduction and an AR reduction.
|
||||
/// </summary>
|
||||
[Authorize(Policy = AppConstants.Policies.CompanyAdminOnly)]
|
||||
public class CreditMemosController : Controller
|
||||
@@ -23,17 +27,20 @@ public class CreditMemosController : Controller
|
||||
private readonly ITenantContext _tenantContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly ILogger<CreditMemosController> _logger;
|
||||
private readonly IAccountBalanceService _accountBalanceService;
|
||||
|
||||
public CreditMemosController(
|
||||
IUnitOfWork unitOfWork,
|
||||
ITenantContext tenantContext,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
ILogger<CreditMemosController> logger)
|
||||
ILogger<CreditMemosController> logger,
|
||||
IAccountBalanceService accountBalanceService)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_tenantContext = tenantContext;
|
||||
_userManager = userManager;
|
||||
_logger = logger;
|
||||
_accountBalanceService = accountBalanceService;
|
||||
}
|
||||
|
||||
/// <summary>Lists all credit memos for the current company with optional status and text filters.</summary>
|
||||
@@ -245,6 +252,20 @@ public class CreditMemosController : Controller
|
||||
await _unitOfWork.Invoices.UpdateAsync(invoice);
|
||||
}
|
||||
|
||||
// GL: DR 4950 Sales Discounts (contra-revenue) / CR AR.
|
||||
// The dynamic report computation attributes credit memo applications to both
|
||||
// accounts already; this call keeps Account.CurrentBalance in sync for
|
||||
// RecalculateAllAsync and any tools that read it directly.
|
||||
var arAcct = await _unitOfWork.Accounts.FirstOrDefaultAsync(
|
||||
a => a.AccountSubType == AccountSubType.AccountsReceivable && a.IsActive);
|
||||
var discountAcct = await _unitOfWork.Accounts.FirstOrDefaultAsync(
|
||||
a => a.AccountNumber == "4950" && a.IsActive)
|
||||
?? await _unitOfWork.Accounts.FirstOrDefaultAsync(
|
||||
a => a.AccountType == AccountType.Revenue && a.IsActive
|
||||
&& a.Name.ToLower().Contains("discount"));
|
||||
await _accountBalanceService.DebitAsync(discountAcct?.Id, applyAmount);
|
||||
await _accountBalanceService.CreditAsync(arAcct?.Id, applyAmount);
|
||||
|
||||
await _unitOfWork.CompleteAsync();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user