faturas_4docs/Compliance/Services/ValidationRules/SeasonalTariffValidationRule.cs

108 lines
4.5 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 SeasonalTariffValidationRule(IDistributorRepository distributorRepository) : IValidationRule
{
private readonly IDistributorRepository _distributorRepository = distributorRepository;
private const string RULE_NAME = "Seasonal Tariff Validation";
private const string TUSD_ERROR = "TUSD amount with seasonal adjustment doesn't match the expected value";
private const string TE_ERROR = "TE amount with seasonal adjustment doesn't match the expected value";
private const string SEASON_ERROR = "Incorrect season applied for the billing period";
private const decimal TOLERANCE = 0.01m;
public int Priority => 4; // Run after reactive energy validation
public async Task<ValidationResult> ValidateAsync(
BillComplianceRequest request,
DistributorInformation distributorInfo,
Client clientInfo)
{
if (!ShouldApplySeasonalTariff(request))
{
return new ValidationResult
{
IsValid = true,
RuleName = RULE_NAME,
Message = string.Empty
};
}
var seasonalInfo = await _distributorRepository.GetSeasonalTariffInformationAsync(
request.DistributorName,
request.ConsumerGroup,
request.Month) ?? throw new InvalidOperationException("Seasonal tariff information not found");
var tariffInfo = await _distributorRepository.GetTariffInformationAsync(
request.DistributorName,
request.Month) ?? throw new InvalidOperationException("Tariff information not found");
var isValid = true;
var message = new StringBuilder();
var billingPeriod = DateOnly.ParseExact(request.Month, "MM/yyyy");
var isDrySeason = IsInDrySeason(billingPeriod, seasonalInfo);
var seasonalMultiplier = isDrySeason ?
seasonalInfo.DrySeasonMultiplier :
seasonalInfo.WetSeasonMultiplier;
if (request.AppliedSeason != (isDrySeason ? "DRY" : "WET"))
{
isValid = false;
message.AppendLine($"{SEASON_ERROR}. Expected: {(isDrySeason ? "DRY" : "WET")}, Found: {request.AppliedSeason}");
}
// Validate TUSD with seasonal adjustment
var expectedTUSD = request.ConsumptionAmount * tariffInfo.TUSDValue * seasonalMultiplier;
if (Math.Abs(request.SeasonalTUSDAmount - expectedTUSD) > TOLERANCE)
{
isValid = false;
message.AppendLine($"{TUSD_ERROR}. Expected: {expectedTUSD:F2}, Found: {request.SeasonalTUSDAmount:F2}");
}
// Validate TE with seasonal adjustment
var expectedTE = request.ConsumptionAmount * tariffInfo.TEValue * seasonalMultiplier;
if (Math.Abs(request.SeasonalTEAmount - expectedTE) > TOLERANCE)
{
isValid = false;
message.AppendLine($"{TE_ERROR}. Expected: {expectedTE:F2}, Found: {request.SeasonalTEAmount:F2}");
}
return new ValidationResult
{
IsValid = isValid,
RuleName = RULE_NAME,
Message = message.ToString().TrimEnd()
};
}
private static bool ShouldApplySeasonalTariff(BillComplianceRequest request)
{
// According to ANEEL, seasonal tariffs typically apply to irrigation/rural consumers
return request.ConsumerGroup.ToUpper() switch
{
"RURAL" => true,
"IRRIGATION" => true,
"AQUACULTURE" => true,
_ => false
};
}
private static bool IsInDrySeason(DateOnly date, SeasonalTariffInformation seasonalInfo)
{
// Handle year wrap-around case
if (seasonalInfo.DrySeasonStart.Month > seasonalInfo.DrySeasonEnd.Month)
{
return date.Month >= seasonalInfo.DrySeasonStart.Month ||
date.Month <= seasonalInfo.DrySeasonEnd.Month;
}
return date.Month >= seasonalInfo.DrySeasonStart.Month &&
date.Month <= seasonalInfo.DrySeasonEnd.Month;
}
}
}