faturas_4docs/Compliance/Services/ValidationRules/PaymentTermsValidationRule.cs

107 lines
4.4 KiB
C#

using System;
using System.Threading.Tasks;
using System.Text;
using Compliance.Domain.Models;
using Compliance.DTOs;
using Compliance.Infrastructure.Repositories;
namespace Compliance.Services.ValidationRules
{
public class PaymentTermsValidationRule(IDistributorRepository distributorRepository) : IValidationRule
{
private readonly IDistributorRepository _distributorRepository = distributorRepository;
private const string RULE_NAME = "Payment Terms Validation";
private const string DUE_DATE_ERROR = "Due date doesn't meet minimum required days";
private const string LATE_FEE_ERROR = "Late payment fee calculation is incorrect";
private const string INTEREST_ERROR = "Interest calculation is incorrect";
private const string PARTIAL_ERROR = "Partial payment not allowed for this consumer";
private const decimal TOLERANCE = 0.01m;
public int Priority => 9; // Run after additional charges validation
public async Task<ValidationResult> ValidateAsync(
BillComplianceRequest request,
DistributorInformation distributorInfo,
Client clientInfo)
{
var termsInfo = await _distributorRepository.GetPaymentTermsInformationAsync(
request.DistributorName,
request.ConsumerGroup,
request.Month) ?? throw new InvalidOperationException("Payment terms information not found");
var isValid = true;
var message = new StringBuilder();
// Get applicable minimum due days
var minimumDueDays = termsInfo.GroupSpecificDueDays.TryGetValue(
request.ConsumerGroup.ToLower(),
out var specialDays) ? specialDays : termsInfo.MinimumDueDays;
// Validate due date
var daysUntilDue = (request.DueDate - request.IssueDate).Days;
if (daysUntilDue < minimumDueDays)
{
isValid = false;
message.AppendLine($"{DUE_DATE_ERROR}. Minimum: {minimumDueDays}, Found: {daysUntilDue}");
}
// Validate late payment charges if applicable
if (request.PaymentDate > request.DueDate &&
!termsInfo.ExemptFromLateCharges.Contains(request.ConsumerGroup.ToLower()))
{
// Validate late payment fee
var expectedLateFee = CalculateLateFee(request.BillTotalBeforeLateCharges, termsInfo.LatePaymentFeePercentage);
if (Math.Abs(request.LatePaymentFee - expectedLateFee) > TOLERANCE)
{
isValid = false;
message.AppendLine($"{LATE_FEE_ERROR}. Expected: {expectedLateFee:F2}, Found: {request.LatePaymentFee:F2}");
}
// Validate interest
var expectedInterest = CalculateInterest(
request.BillTotalBeforeLateCharges,
request.DueDate,
request.PaymentDate,
termsInfo.MonthlyInterestRate);
if (Math.Abs(request.InterestAmount - expectedInterest) > TOLERANCE)
{
isValid = false;
message.AppendLine($"{INTEREST_ERROR}. Expected: {expectedInterest:F2}, Found: {request.InterestAmount:F2}");
}
}
// Validate partial payment if applicable
if (request.IsPartialPayment && !termsInfo.AllowPartialPayments)
{
isValid = false;
message.AppendLine(PARTIAL_ERROR);
}
return new ValidationResult
{
IsValid = isValid,
RuleName = RULE_NAME,
Message = message.ToString().TrimEnd()
};
}
private static decimal CalculateLateFee(decimal amount, decimal feePercentage)
{
return amount * (feePercentage / 100m);
}
private static decimal CalculateInterest(
decimal amount,
DateTime dueDate,
DateTime paymentDate,
decimal monthlyRate)
{
var monthsLate = ((paymentDate.Year - dueDate.Year) * 12) +
paymentDate.Month - dueDate.Month +
(paymentDate.Day >= dueDate.Day ? 0 : -1);
if (monthsLate <= 0) return 0;
return amount * (monthlyRate / 100m) * monthsLate;
}
}
}