Untitled

 avatar
unknown
plain_text
7 days ago
16 kB
7
Indexable
using KMD.Elements.MDP.ConnectionPoints.Application.Dtos.MeteringPoints;
using KMD.Elements.MDP.ConnectionPoints.Application.MeteringPointProcesses.ChangeFormulaProcess.Mappers;
using KMD.Elements.MDP.ConnectionPoints.Application.MeteringPointProcesses.Exceptions;
using KMD.Elements.MDP.ConnectionPoints.Application.Services.ExternalCommunication.ConnectionPoints;
using KMD.Elements.MDP.ConnectionPoints.Application.Services.ExternalCommunication.MeterFrames;
using KMD.Elements.MDP.ConnectionPoints.Application.Services.ExternalCommunication.MeteringPoints;
using KMD.Elements.MDP.ConnectionPoints.Application.Services.ExternalCommunication.MeteringPointsV2;
using KMD.Elements.MDP.ConnectionPoints.Contracts.ChangeFormulaProcessCommand;
using KMD.Elements.MDP.ConnectionPoints.Core.Constants.MeteringPoints;
using KMD.Elements.MDP.ConnectionPoints.Core.Exceptions;
using KMD.Elements.MDP.ConnectionPoints.Models.Models.MeteringPointIds;
using KMD.Elements.MDP.ConnectionPoints.Models.Models.MeteringPointProcesses.ValueObjects;
using SupplyType = KMD.Elements.MDP.ConnectionPoints.Models.Models.Common.Enums.SupplyType;

namespace KMD.Elements.MDP.ConnectionPoints.Application.MeteringPointProcesses.ChangeFormulaProcess.Commands.StartChangeFormulaProcess;

public class ChangeFormulaProcessCommandFactory(
    IMeterFramesService meterFramesService,
    IConnectionPointsService connectionPointsService,
    IMeteringPointsV2Service meteringPointsService,
    IMeteringPointsService electricityMeteringPointsService)
    : IChangeFormulaProcessCommandFactory
{
    private ChangeFormulaPayloadBuilder _payloadBuilder = null!;
    private string? _oldSubtypeOfMeteringPoint = null;

    public async Task<StartChangeFormulaProcess> CreateAsync(
        ChangeFormulaProcessCommandPayload payload,
        DateTime timestamp,
        CancellationToken cancellationToken)
    {
        _payloadBuilder = new ChangeFormulaPayloadBuilder(
            payload.MeteringPointVersionId,
            payload.ValidFrom,
            payload.ProcessId ?? Guid.NewGuid(),
            payload.StartedBy,
            payload.OnBehalfOf,
            DateTime.UtcNow,
            ChangeFormulaParentProcessMapper.Map(payload.ParentProcess));

        var meteringPointId = MeteringPointIdGenerator.ExtractMeteringPointId(payload.MeteringPointVersionId);

        //if (meteringPointId.SupplyType == SupplyType.Electricity)
        //{
        //    throw new NotSupportedException($"SupplyType {meteringPointId.SupplyType} is not supported");
        //}

        await BuildUpdatePayload(meteringPointId.Id, meteringPointId.SupplyType, payload, cancellationToken);

        var formulaPayload = await GetFormulaPayload(payload.MeteringPointVersionId, meteringPointId.SupplyType, cancellationToken);
        await BuildFormulaPayload(payload, formulaPayload, meteringPointId.SupplyType, cancellationToken);

        return _payloadBuilder.Build();
    }

    private async Task BuildUpdatePayload(
        string meteringPointId,
        SupplyType supplyType,
        ChangeFormulaProcessCommandPayload payload,
        CancellationToken cancellationToken)
    {
        switch (supplyType)
        {
            case SupplyType.Water:
                await BuildUpdatePayloadForWater(meteringPointId, payload, cancellationToken);
                break;
            case SupplyType.Heating:
                await BuildUpdatePayloadForHeating(meteringPointId, payload, cancellationToken);
                break;
            case SupplyType.Electricity:
                await BuildUpdatePayloadForElectricity(meteringPointId, payload, cancellationToken);
                break;
        }
    }

    private async Task BuildFormulaPayload(
        ChangeFormulaProcessCommandPayload payload,
        FormulaVersionDto? oldFormula,
        SupplyType supplyType,
        CancellationToken cancellationToken)
    {
        if (oldFormula is null)
        {
            _payloadBuilder.WithFormula(payload.Formula);
            return;
        }

        if (!payload.Formula.IsSimpleFormula && !HasVirtualFormulaChanged(payload, oldFormula))
        {
            return;
        }

        if (payload.Formula.IsSimpleFormula && payload.Formula.MeterFrameId == oldFormula.MeterFrameId && payload.Formula.RegisterRequirementId == oldFormula.RegisterRequirementId)
        {
            return;
        }

        if (payload.Formula.MeterFrameId != oldFormula.MeterFrameId)
        {
            var meter = await BuildMeterPayload(payload, cancellationToken) ?? GetDefaultMeterData(supplyType);
            _payloadBuilder.WithMeterPayload(meter);
        }
        else if (payload.Formula.RegisterRequirementId != oldFormula.RegisterRequirementId)
        {
            var meter = await BuildMeterPayload(payload, cancellationToken) ?? GetDefaultMeterData(supplyType);
            _payloadBuilder.WithMeterInUpdatePayload(meter);
        }

        _payloadBuilder.WithFormula(payload.Formula);
    }

    private static bool HasVirtualFormulaChanged(
        ChangeFormulaProcessCommandPayload payload,
        FormulaVersionDto oldFormula)
    {
        if (payload.Formula.FormulaExpression != oldFormula.Formula)
        {
            return true;
        }

        if (payload.Formula.FormulaParameters == null && oldFormula.FormulaParameters == null)
        {
            return false;
        }

        if (payload.Formula.FormulaParameters == null || oldFormula.FormulaParameters == null)
        {
            return true;
        }

        if (payload.Formula.FormulaParameters.Count != oldFormula.FormulaParameters.Count)
        {
            return true;
        }

        var newFormulaParameters = payload.Formula.FormulaParameters.OrderBy(x => x.Name).ToList();
        var oldFormulaParameters = oldFormula.FormulaParameters.OrderBy(x => x.Name).ToList();

        return newFormulaParameters.Where((x, y) => AreFormulaParametersDifferent(x, oldFormulaParameters[y])).Any();
    }

    private static bool AreFormulaParametersDifferent(FormulaParameter newFormula, FormulaParameterDto oldFormula)
        => newFormula.Name != oldFormula.Name ||
           newFormula.FormulaParameterType.ToString() != oldFormula.FormulaParameterType.ToString() ||
           newFormula.Description != oldFormula.Description ||
           newFormula.MeteringPointTypeCode != oldFormula.MeteringPointTypeCode;

    private async Task<MeterUpdatePayload?> BuildMeterPayload(ChangeFormulaProcessCommandPayload payload, CancellationToken cancellationToken)
    {
        var meterFrameId = payload.Formula.MeterFrameId!.Value;
        var registerRequirementId = payload.Formula.RegisterRequirementId!.Value;

        var meterInformation = await meterFramesService.GetMeterInformationByMeterFrameRegisterRequirementAsync(
            meterFrameId,
            registerRequirementId,
            cancellationToken);

        if (string.IsNullOrEmpty(payload.SubTypeOfMeteringPoint)!)
        {
            if (payload.SubTypeOfMeteringPoint != _oldSubtypeOfMeteringPoint)
            {
                var functionCode = GetFunctionCode(_oldSubtypeOfMeteringPoint, payload.SubTypeOfMeteringPoint);
                return new MeterUpdatePayload()
                {
                    ConversionFactor = meterInformation!.ConversionFactor,
                    NumberOfDigits = meterInformation.NumberOfDigits,
                    UnitType = meterInformation.UnitType,
                    MeterNumber = meterInformation.MeterNumber,
                    MeterReadingType = meterInformation.MeterReadingType,
                    Function = functionCode,
                    SubtypeOfMeteringPoint = payload.SubTypeOfMeteringPoint
                };
            }

            return null;
        }

        if (meterInformation is null)
        {
            return null;
        }

        return new MeterUpdatePayload()
        {
            ConversionFactor = meterInformation!.ConversionFactor,
            NumberOfDigits = meterInformation.NumberOfDigits,
            UnitType = meterInformation.UnitType,
            MeterNumber = meterInformation.MeterNumber,
            MeterReadingType = meterInformation.MeterReadingType
        };
    }

    private string? GetFunctionCode(string? oldSubtypeOfMeteringPoint, string? newSubTypeOfMeteringPoint)
    {
        if ((oldSubtypeOfMeteringPoint == SubTypesOfMeteringPoint.D02_Virtual || oldSubtypeOfMeteringPoint == SubTypesOfMeteringPoint.D03_Calculated) &&
            newSubTypeOfMeteringPoint == SubTypesOfMeteringPoint.D01_Physical)
        {
            return "2";
        }

        if ((newSubTypeOfMeteringPoint == SubTypesOfMeteringPoint.D02_Virtual || newSubTypeOfMeteringPoint == SubTypesOfMeteringPoint.D03_Calculated) &&
              oldSubtypeOfMeteringPoint == SubTypesOfMeteringPoint.D01_Physical)
        {
            return "3";
        }

        return null;
    }

    private static MeterUpdatePayload GetDefaultMeterData(SupplyType supplyType)
        => new()
        {
            ConversionFactor = 1,
            NumberOfDigits = "1",
            UnitType = supplyType is SupplyType.Water ? "M3" : "MWH",
            MeterNumber = "FIKTIV",
            MeterReadingType = "D01"
        };

    private async Task BuildUpdatePayloadForWater(
        string meteringPointId,
        ChangeFormulaProcessCommandPayload payload,
        CancellationToken cancellationToken)
    {
        var meteringPointVersion = await meteringPointsService.GetWaterMeteringPointVersionAsync(meteringPointId, cancellationToken);

        if (meteringPointVersion == null)
        {
            throw new MeteringPointNotFoundException($"Metering point with id {meteringPointId} not found in Metering Points domain.");
        }

        if (meteringPointVersion.ConnectionPointId is null)
        {
            throw new MeteringPointProcessValidationException("Connection point id is missing on metering point.");
        }

        var connectionPointNumber = await GetConnectionPointNumber(meteringPointVersion.ConnectionPointId.Value, cancellationToken);

        if (connectionPointNumber is null)
        {
            throw new MeteringPointProcessValidationException("Connection point number is missing on metering point.");
        }

        _payloadBuilder.WithUnitType(payload.UnitType, meteringPointVersion.ProductUnitType)
            .WithMeterReadingOccurrence(payload.MeterReadingOccurrence, meteringPointVersion.MeterReadingOccurrence)
            .WithConnectionPointId(meteringPointVersion.ConnectionPointId!.Value)
            .WithNoMeterAndMeterSize(meteringPointVersion.NoMeter, meteringPointVersion.MeterSize)
            .WithConnectionPointNumber(connectionPointNumber)
            .WithTypeOfMeteringPoint(meteringPointVersion.TypeOfMeteringPoint!);
    }

    private async Task BuildUpdatePayloadForHeating(
        string meteringPointId,
        ChangeFormulaProcessCommandPayload payload,
        CancellationToken cancellationToken)
    {
        var meteringPointVersion = await meteringPointsService.GetHeatingMeteringPointVersionAsync(meteringPointId, cancellationToken);

        if (meteringPointVersion == null)
        {
            throw new MeteringPointNotFoundException($"Metering point with id {meteringPointId} not found in Metering Points domain.");
        }

        if (meteringPointVersion.ConnectionPointId is null)
        {
            throw new MeteringPointProcessValidationException("Connection point id is missing on metering point.");
        }

        var connectionPointNumber = await GetConnectionPointNumber(meteringPointVersion.ConnectionPointId!.Value, cancellationToken);

        if (connectionPointNumber is null)
        {
            throw new MeteringPointProcessValidationException("Connection point number is missing on metering point.");
        }

        _payloadBuilder.WithUnitType(payload.UnitType, meteringPointVersion.ProductUnitType)
            .WithMeterReadingOccurrence(payload.MeterReadingOccurrence, meteringPointVersion.MeterReadingOccurrence)
            .WithConnectionPointId(meteringPointVersion.ConnectionPointId!.Value)
            .WithConnectionPointNumber(connectionPointNumber)
            .WithTypeOfMeteringPoint(meteringPointVersion.TypeOfMeteringPoint!);
    }

    private async Task BuildUpdatePayloadForElectricity(
        string meteringPointId,
        ChangeFormulaProcessCommandPayload payload,
        CancellationToken cancellationToken)
    {
        var meteringPointVersion = await electricityMeteringPointsService.GetMeteringPointVersionByIdAsync(meteringPointId, cancellationToken);

        if (meteringPointVersion == null)
        {
            throw new MeteringPointNotFoundException($"Metering point with id {meteringPointId} not found in Metering Points domain.");
        }

        _oldSubtypeOfMeteringPoint = meteringPointVersion.SubTypeOfMeteringPoint;
        if (!ShouldCreateBrs006(meteringPointVersion.SubTypeOfMeteringPoint, payload.SubTypeOfMeteringPoint))
        {
            return;
        }

        if (meteringPointVersion.ConnectionPointId is null)
        {
            throw new MeteringPointProcessValidationException("Connection point id is missing on metering point.");
        }

        var connectionPointNumber = await GetConnectionPointNumber(meteringPointVersion.ConnectionPointId!.Value, cancellationToken);

        if (connectionPointNumber is null)
        {
            throw new MeteringPointProcessValidationException("Connection point number is missing on metering point.");
        }

        _payloadBuilder.WithUnitType(payload.UnitType, meteringPointVersion.ProductUnitType)
            .WithMeterReadingOccurrence(payload.MeterReadingOccurrence, meteringPointVersion.MeterReadingOccurrence)
            .WithConnectionPointId(meteringPointVersion.ConnectionPointId!.Value)
            .WithConnectionPointNumber(connectionPointNumber)
            .WithTypeOfMeteringPoint(meteringPointVersion.TypeOfMeteringPoint!);
    }

    private bool ShouldCreateBrs006(string? oldSubtypeOfMeteringPoint, string? newSubTypeOfMeteringPoint)
    {
        if (oldSubtypeOfMeteringPoint == SubTypesOfMeteringPoint.D02_Virtual && newSubTypeOfMeteringPoint == SubTypesOfMeteringPoint.D03_Calculated)
        {
            return true;
        }

        if (oldSubtypeOfMeteringPoint == SubTypesOfMeteringPoint.D03_Calculated && newSubTypeOfMeteringPoint == SubTypesOfMeteringPoint.D02_Virtual)
        {
            return true;
        }

        return false;
    }

    private async Task<FormulaVersionDto?> GetFormulaPayload(
        string meteringPointVersionId,
        SupplyType supplyType,
        CancellationToken cancellationToken)
    {
        switch (supplyType)
        {
            case SupplyType.Heating:
            {
                var dto = await meteringPointsService.GetHeatingFormulaVersionAsync(meteringPointVersionId, cancellationToken);
                return dto;
            }

            case SupplyType.Water:
            {
                var dto = await meteringPointsService.GetWaterFormulaVersionAsync(meteringPointVersionId, cancellationToken);
                return dto;
            }

            case SupplyType.Electricity:
            default:
                throw new ArgumentOutOfRangeException(nameof(supplyType), supplyType, null);
        }
    }

    private async Task<string?> GetConnectionPointNumber(
        Guid connectionPointId,
        CancellationToken cancellationToken)
    {
        var connectionPoint = await connectionPointsService.GetConnectionPointAsync(connectionPointId, cancellationToken);
        return connectionPoint?.ConnectionPointNumber;
    }
}
Leave a Comment