97 lines
3.7 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 SubsidyValidationRule(IDistributorRepository distributorRepository) : IValidationRule
{
private readonly IDistributorRepository _distributorRepository = distributorRepository;
private const string RULE_NAME = "Subsidy and Discount Validation";
private const string AMOUNT_ERROR = "Discount amount doesn't match the expected value";
private const string ELIGIBILITY_ERROR = "Consumer not eligible for applied discount";
private const string CONSUMPTION_ERROR = "Consumption outside eligible range for discount";
private const decimal TOLERANCE = 0.01m;
public int Priority => 6; // Run after flag tariff validation
public async Task<ValidationResult> ValidateAsync(
BillComplianceRequest request,
DistributorInformation distributorInfo,
Client clientInfo)
{
var subsidyInfo = await _distributorRepository.GetSubsidyInformationAsync(
request.DistributorName,
request.ConsumerGroup,
request.Month) ?? throw new InvalidOperationException("Subsidy information not found");
var isValid = true;
var message = new StringBuilder();
// Check consumption eligibility
if (request.ConsumptionAmount < subsidyInfo.Consumption.MinConsumption ||
(subsidyInfo.Consumption.MaxConsumption > 0 && request.ConsumptionAmount > subsidyInfo.Consumption.MaxConsumption))
{
if (request.DiscountAmount != 0)
{
isValid = false;
message.AppendLine($"{CONSUMPTION_ERROR}. Range: {subsidyInfo.Consumption.MinConsumption}-{subsidyInfo.Consumption.MaxConsumption} kWh");
}
return new ValidationResult
{
IsValid = isValid,
RuleName = RULE_NAME,
Message = message.ToString().TrimEnd()
};
}
// Get applicable discount rate
var discountRate = subsidyInfo.GroupDiscounts.TryGetValue(request.ConsumerGroup.ToLower(), out var specialRate)
? specialRate
: subsidyInfo.BaseDiscountPercentage;
// Calculate expected discount
var expectedDiscount = CalculateDiscount(request, subsidyInfo, discountRate);
if (Math.Abs(request.DiscountAmount - expectedDiscount) > TOLERANCE)
{
isValid = false;
message.AppendLine($"{AMOUNT_ERROR}. Expected: {expectedDiscount:F2}, Found: {request.DiscountAmount:F2}");
}
return new ValidationResult
{
IsValid = isValid,
RuleName = RULE_NAME,
Message = message.ToString().TrimEnd()
};
}
private static decimal CalculateDiscount(
BillComplianceRequest request,
SubsidyInformation subsidyInfo,
decimal discountRate)
{
var baseAmount = 0m;
if (subsidyInfo.ApplyToTUSD)
{
baseAmount += request.TUSDAmount;
}
if (subsidyInfo.ApplyToTE)
{
baseAmount += request.TEAmount;
}
if (subsidyInfo.ApplyToFlags)
{
baseAmount += request.FlagAmount;
}
return baseAmount * (discountRate / 100m);
}
}
}