126 lines
5.2 KiB
C#
126 lines
5.2 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 ReadingImpedimentValidationRule(IDistributorRepository distributorRepository) : IValidationRule
|
|
{
|
|
private readonly IDistributorRepository _distributorRepository = distributorRepository;
|
|
private const string RULE_NAME = "Reading Impediment Validation";
|
|
|
|
// Art. 231 - Impediment documentation requirements
|
|
private const int MAX_DAYS_WITHOUT_NOTIFICATION = 15;
|
|
private const int MAX_CONSECUTIVE_IMPEDIMENTS = 3;
|
|
private const string NOTIFICATION_ERROR = "Customer must be notified within 15 days of impediment";
|
|
private const string CONSECUTIVE_ERROR = "Maximum consecutive impediments exceeded";
|
|
private const string DOCUMENTATION_ERROR = "Insufficient impediment documentation";
|
|
private const string RESOLUTION_ERROR = "No resolution attempts documented";
|
|
private const string EVIDENCE_ERROR = "Photo evidence required for access impediment";
|
|
|
|
public int Priority => 1; // High priority as it affects reading validation
|
|
|
|
public async Task<ValidationResult> ValidateAsync(
|
|
BillComplianceRequest request,
|
|
DistributorInformation distributorInfo,
|
|
Client clientInfo)
|
|
{
|
|
if (!request.HasReadingImpediment)
|
|
return ValidationResult.Success(RULE_NAME);
|
|
|
|
var impedimentInfo = await _distributorRepository.GetReadingImpedimentInfoAsync(
|
|
request.SmartCode,
|
|
request.CurrentReadingDate) ?? throw new InvalidOperationException("Impediment information not found");
|
|
|
|
var errors = new List<string>();
|
|
|
|
ValidateImpedimentDocumentation(impedimentInfo, errors);
|
|
ValidateCustomerNotification(impedimentInfo, errors);
|
|
ValidateConsecutiveImpediments(impedimentInfo, errors);
|
|
ValidateResolutionAttempts(impedimentInfo, errors);
|
|
ValidateAlternativeReading(request, impedimentInfo, errors);
|
|
|
|
return errors.Count == 0
|
|
? ValidationResult.Success(RULE_NAME)
|
|
: ValidationResult.Failure(RULE_NAME, errors);
|
|
}
|
|
|
|
private static void ValidateImpedimentDocumentation(ReadingImpedimentInfo info, List<string> errors)
|
|
{
|
|
if (string.IsNullOrEmpty(info.ImpedimentCode) ||
|
|
string.IsNullOrEmpty(info.Description) ||
|
|
info.ReportDate == default ||
|
|
string.IsNullOrEmpty(info.ReportedBy))
|
|
{
|
|
errors.Add(DOCUMENTATION_ERROR);
|
|
}
|
|
|
|
// Art. 231 §1 - Photo evidence requirement
|
|
if (info.ImpedimentCode.Contains("ACCESS", StringComparison.OrdinalIgnoreCase) &&
|
|
info.PhotoEvidence.Count == 0)
|
|
{
|
|
errors.Add(EVIDENCE_ERROR);
|
|
}
|
|
}
|
|
|
|
private static void ValidateCustomerNotification(ReadingImpedimentInfo info, List<string> errors)
|
|
{
|
|
if (info.RequiresCustomerAction)
|
|
{
|
|
if (!info.CustomerNotificationDate.HasValue)
|
|
{
|
|
errors.Add(NOTIFICATION_ERROR);
|
|
}
|
|
else
|
|
{
|
|
var notificationDelay = (info.CustomerNotificationDate.Value - info.ReportDate).Days;
|
|
if (notificationDelay > MAX_DAYS_WITHOUT_NOTIFICATION)
|
|
{
|
|
errors.Add($"{NOTIFICATION_ERROR}. Actual delay: {notificationDelay} days");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateConsecutiveImpediments(ReadingImpedimentInfo info, List<string> errors)
|
|
{
|
|
if (info.ConsecutiveOccurrences > MAX_CONSECUTIVE_IMPEDIMENTS)
|
|
{
|
|
errors.Add($"{CONSECUTIVE_ERROR}. Current consecutive: {info.ConsecutiveOccurrences}");
|
|
}
|
|
}
|
|
|
|
private static void ValidateResolutionAttempts(ReadingImpedimentInfo info, List<string> errors)
|
|
{
|
|
if (info.ConsecutiveOccurrences > 1 &&
|
|
string.IsNullOrEmpty(info.ResolutionAttempts))
|
|
{
|
|
errors.Add(RESOLUTION_ERROR);
|
|
}
|
|
}
|
|
|
|
private static void ValidateAlternativeReading(
|
|
BillComplianceRequest request,
|
|
ReadingImpedimentInfo info,
|
|
List<string> errors)
|
|
{
|
|
// Art. 232 - Alternative reading methods
|
|
if (string.IsNullOrEmpty(info.AlternativeReadingMethod))
|
|
{
|
|
errors.Add("Alternative reading method must be documented");
|
|
}
|
|
else if (info.AlternativeReadingMethod.Equals("ESTIMATION",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Ensure estimation is properly justified
|
|
if (string.IsNullOrEmpty(request.EstimationJustification))
|
|
{
|
|
errors.Add("Estimation justification required for impediment-based reading");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |