117 lines
4.2 KiB
C#
117 lines
4.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 FlagTariffValidationRule(IDistributorRepository distributorRepository) : IValidationRule
|
|
{
|
|
private readonly IDistributorRepository _distributorRepository = distributorRepository;
|
|
private const string RULE_NAME = "Flag Tariff Validation";
|
|
private const string FLAG_ERROR = "Flag tariff amount doesn't match the expected value for the current flag color";
|
|
private const string EXEMPT_ERROR = "Flag tariff charged to exempt consumer";
|
|
private const string COLOR_ERROR = "Invalid flag color applied";
|
|
private const decimal TOLERANCE = 0.01m;
|
|
private const decimal KWH_BLOCK = 100m; // Flag tariff is applied per 100 kWh
|
|
public int Priority => 4; // Run after tariff component validation
|
|
|
|
public async Task<ValidationResult> ValidateAsync(
|
|
BillComplianceRequest request,
|
|
DistributorInformation distributorInfo,
|
|
Client clientInfo)
|
|
{
|
|
var flagInfo = await _distributorRepository.GetFlagTariffInformationAsync(
|
|
request.DistributorName,
|
|
request.Month) ?? throw new InvalidOperationException("Flag tariff information not found");
|
|
|
|
var isValid = true;
|
|
var message = new StringBuilder();
|
|
|
|
// Check if consumer is exempt
|
|
if (flagInfo.ExemptGroups.Contains(request.ConsumerGroup.ToLower()))
|
|
{
|
|
if (request.FlagAmount != 0)
|
|
{
|
|
isValid = false;
|
|
message.AppendLine(EXEMPT_ERROR);
|
|
}
|
|
return new ValidationResult
|
|
{
|
|
IsValid = isValid,
|
|
RuleName = RULE_NAME,
|
|
Message = message.ToString().TrimEnd()
|
|
};
|
|
}
|
|
|
|
// Validate flag color
|
|
if (!IsValidFlagColor(request.AppliedFlag))
|
|
{
|
|
isValid = false;
|
|
message.AppendLine($"{COLOR_ERROR}. Found: {request.AppliedFlag}");
|
|
}
|
|
|
|
// Calculate expected flag amount
|
|
var expectedFlagAmount = CalculateFlagAmount(
|
|
request.ConsumptionAmount,
|
|
flagInfo.FlagColor,
|
|
flagInfo.FlagValue,
|
|
request.BillingDays,
|
|
flagInfo.ApplyPartialMonth,
|
|
flagInfo.ValidFromDay);
|
|
|
|
if (Math.Abs(request.FlagAmount - expectedFlagAmount) > TOLERANCE)
|
|
{
|
|
isValid = false;
|
|
message.AppendLine($"{FLAG_ERROR}. Expected: {expectedFlagAmount:F2}, Found: {request.FlagAmount:F2}");
|
|
}
|
|
|
|
return new ValidationResult
|
|
{
|
|
IsValid = isValid,
|
|
RuleName = RULE_NAME,
|
|
Message = message.ToString().TrimEnd()
|
|
};
|
|
}
|
|
|
|
private static bool IsValidFlagColor(string flagColor)
|
|
{
|
|
return flagColor.ToUpper() switch
|
|
{
|
|
"GREEN" => true,
|
|
"YELLOW" => true,
|
|
"RED1" => true,
|
|
"RED2" => true,
|
|
_ => false
|
|
};
|
|
}
|
|
|
|
private static decimal CalculateFlagAmount(
|
|
decimal consumption,
|
|
string flagColor,
|
|
decimal flagValue,
|
|
int billingDays,
|
|
bool applyPartialMonth,
|
|
int validFromDay)
|
|
{
|
|
if (flagColor.Equals("GREEN", StringComparison.CurrentCultureIgnoreCase)) return 0;
|
|
|
|
// Calculate number of 100 kWh blocks
|
|
var blocks = Math.Ceiling(consumption / KWH_BLOCK);
|
|
|
|
// If partial month application is enabled, prorate the flag value
|
|
if (applyPartialMonth)
|
|
{
|
|
var applicableDays = billingDays - (validFromDay - 1);
|
|
if (applicableDays < billingDays)
|
|
{
|
|
return blocks * flagValue * (applicableDays / (decimal)billingDays);
|
|
}
|
|
}
|
|
|
|
return blocks * flagValue;
|
|
}
|
|
}
|
|
} |