faturas_4docs/Compliance/Services/ValidationRules/GroupSpecificRulesValidationRule.cs

152 lines
6.1 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 GroupSpecificRulesValidationRule(IDistributorRepository distributorRepository) : IValidationRule
{
private readonly IDistributorRepository _distributorRepository = distributorRepository;
private const string RULE_NAME = "Group-Specific Rules Validation";
// Art. 297-299 - Rural and Irrigation Requirements
private const decimal MIN_IRRIGATION_DISCOUNT = 0.10m;
private const decimal MAX_IRRIGATION_DISCOUNT = 0.90m;
private const string SCHEDULE_ERROR = "Irrigation schedule must be documented for irrigation consumers";
private const string SEASON_ERROR = "Invalid seasonal period configuration";
private const string DISCOUNT_ERROR = "Invalid irrigation discount";
private const string ACTIVITY_ERROR = "Activity-specific discount not properly documented";
public int Priority => 3; // Run after basic and consumption validation
public async Task<ValidationResult> ValidateAsync(
BillComplianceRequest request,
DistributorInformation distributorInfo,
Client clientInfo)
{
var groupRules = await _distributorRepository.GetGroupSpecificRulesInfoAsync(
request.ConsumerGroup,
request.Subgroup,
request.Month) ?? throw new InvalidOperationException("Group rules not found");
var errors = new List<string>();
ValidateRuralClassification(request, groupRules, errors);
ValidateIrrigationRules(request, groupRules, errors);
ValidateSeasonalRules(request, groupRules, errors);
ValidateActivityDiscounts(request, groupRules, errors);
return errors.Count == 0
? ValidationResult.Success(RULE_NAME)
: ValidationResult.Failure(RULE_NAME, errors);
}
private static void ValidateRuralClassification(
BillComplianceRequest request,
GroupSpecificRulesInfo rules,
List<string> errors)
{
if (rules.IsRural)
{
// Art. 297 - Rural consumer validation
if (!rules.SpecialConditions.Any())
{
errors.Add("Rural classification requires documented special conditions");
}
// Validate activity-specific requirements
foreach (var condition in rules.SpecialConditions)
{
if (!rules.ActivityDiscounts.ContainsKey(condition))
{
errors.Add($"Missing discount configuration for rural activity: {condition}");
}
}
}
}
private static void ValidateIrrigationRules(
BillComplianceRequest request,
GroupSpecificRulesInfo rules,
List<string> errors)
{
if (rules.IsIrrigation)
{
// Art. 298 - Irrigation requirements
if (string.IsNullOrEmpty(rules.IrrigationSchedule))
{
errors.Add(SCHEDULE_ERROR);
}
if (!rules.IrrigationDiscount.HasValue)
{
errors.Add(DISCOUNT_ERROR);
}
else if (rules.IrrigationDiscount < MIN_IRRIGATION_DISCOUNT ||
rules.IrrigationDiscount > MAX_IRRIGATION_DISCOUNT)
{
errors.Add($"Irrigation discount ({rules.IrrigationDiscount:P}) outside allowed range " +
$"({MIN_IRRIGATION_DISCOUNT:P}-{MAX_IRRIGATION_DISCOUNT:P})");
}
}
}
private static void ValidateSeasonalRules(
BillComplianceRequest request,
GroupSpecificRulesInfo rules,
List<string> errors)
{
// Art. 299 - Seasonal variations
if (!string.IsNullOrEmpty(rules.Season))
{
if (rules.SeasonStartDate == default || rules.SeasonEndDate == default)
{
errors.Add(SEASON_ERROR);
}
var currentDate = request.CurrentReadingDate;
if (currentDate >= rules.SeasonStartDate && currentDate <= rules.SeasonEndDate)
{
if (rules.SeasonalMultiplier <= 0)
{
errors.Add("Invalid seasonal multiplier");
}
// Validate if seasonal multiplier was correctly applied
var expectedAmount = request.ConsumptionAmount * rules.SeasonalMultiplier;
if (Math.Abs(expectedAmount - request.BillTotalBeforeTaxes) > 0.01m)
{
errors.Add("Seasonal multiplier not correctly applied to consumption amount");
}
}
}
}
private static void ValidateActivityDiscounts(
BillComplianceRequest request,
GroupSpecificRulesInfo rules,
List<string> errors)
{
foreach (var (activity, discount) in rules.ActivityDiscounts)
{
if (discount <= 0 || discount > 1)
{
errors.Add($"{ACTIVITY_ERROR}: Invalid discount percentage for {activity}");
}
// If this activity applies to the current bill
if (request.ApplicableActivities.Contains(activity))
{
var expectedDiscount = request.BillTotalBeforeTaxes * discount;
if (Math.Abs(expectedDiscount - request.ActivityDiscountAmount) > 0.01m)
{
errors.Add($"Activity discount for {activity} not correctly applied");
}
}
}
}
}
}