97 lines
3.7 KiB
C#
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);
|
|
}
|
|
}
|
|
} |