Отвечай на вопросы, используя следующий контекст:
/// <summary>
    /// Тест-драйв
    /// </summary>
    public const string Testdrive = "TESTDRIVE";

    /// <summary>
    /// Коммерческая
    /// </summary>
    public const string Commerce = "COMMERCE";

    /// <summary>
    /// Доставка.
    /// </summary>
    public const string Delivery = "DELIVERY";

    /// <summary>
    /// Такси.
    /// </summary>
    public const string Taxi = PurposeOfUsingCodes.Taxi;

    /// <summary>
    /// Дорожные и специальные ТС.
    /// </summary>
    public const string Special = PurposeOfUsingCodes.Special;

    /// <summary>
    /// Аренда/прокат.
    /// </summary>
    public const string Rental = PurposeOfUsingCodes.Rental;

    /// <summary>
    /// Прочее.
    /// </summary>
    public const string Other = PurposeOfUsingCodes.Other;

    /// <summary>
    /// Перевозки опасных и легковоспламеняющихся грузов.
    /// </summary>
    public const string Danger = PurposeOfUsingCodes.Danger;

    /// <summary>
    /// Работы экстренных и коммунальных служб.
    /// </summary>
    public const string Municipal = PurposeOfUsingCodes.Municipal;

    /// <summary>
    /// Регулярных пассажирских перевозок или перевозок пассажиров по заказам.
    /// </summary>
    public const string Passenger = PurposeOfUsingCodes.Passenger;

    /// <summary>
    /// Учебной езды.
    /// </summary>
    public const string Learn = PurposeOfUsingCodes.Learn;

    /// <summary>
    /// Проверить валидность цели перевозки для юр.лиц.
    /// </summary>
    /// <param name="purposeOfUsing">Код цели перевозки.</param>
    /// <returns>Флаг валидности цели.</returns>
    public static bool Contains(string purposeOfUsing)
        => LegalPurposes.Any(x => x.Equals(purposeOfUsing, StringComparison.OrdinalIgnoreCase));

    private static readonly string[] LegalPurposes =
    [
        Encashment, 
        Ambulance, 
        Carsharing, 
        Testdrive, 
        Commerce, 
        Delivery,
        Rental,
        Special,
        Taxi,
        Danger,
        Municipal,
        Passenger,
        Learn,
        Other
    ];
}


# Code\Constants\RegistrationRegions.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public class RegistrationRegions
    {
        public const string Moskva = "Moskva";
        public const string SanktPeterburg = "SanktPeterburg";
        public const string Kazan = "Kazan";
        public const string NizhniNovgo = "NizhniNovgo";
        public const string CHelyabinsk = "CHelyabinsk";
        public const string Novosibirsk = "Novosibirsk";
        public const string Ekaterinburg = "Ekaterinburg";
        public const string RespuTatar = "RespuTatar";
        public const string Nizhegoblas = "Nizhegoblas";
        public const string CHelyaboblas = "CHelyaboblas";
        public const string Novosoblas = "Novosoblas";
        public const string Sverdoblas = "Sverdoblas";
    }
}


# Code\Constants\SalesChainNumbers.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    internal static class SalesChainNumbers
    {
        /// <summary>
        /// Цепочка продаж для Страховая касса.
        /// </summary>
        public const string StrahKassaSalesChainNumber = "39435";

        /// <summary>
        /// Цепочка продаж для МИКСТЕЛЛ.
        /// </summary>
        public const string MikstellSalesChainNumber = "36820";
    }
}


# Code\Constants\SasApprovalStatusCodes.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public static class SasApprovalStatusCodes
    {
        public const string Accepted = "accepted";
        public const string Rejected = "rejected";
        public const string Urm = "urm";
        public const string NoDecision = "noDecision";
    }
}

# Code\Constants\SasRequestSource.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public static class SasRequestSource
    {
        public const string Constructor = "Constructor";
        public const string Questionnaire = "Questionnaire";
    }
}


# Code\Constants\SexCodes.cs
﻿namespace iRIS.KaskoLink2.Code.Constants;

public class SexCodes
{
    /// <summary>
    /// Мужской.
    /// </summary>
    public const string M = "M";

    /// <summary>
    /// Женский.
    /// </summary>
    public const string F = "F";

    /// <summary>
    /// Трансгендер.
    /// </summary>
    public const string TG = "TG";

    public static readonly string[] AvailableSexCodes = { M, F };
}


# Code\Constants\SexNames.cs
﻿namespace iRIS.KaskoLink2.Code.Constants;

public class SexNames
{
    public const string M = "Мужской";
    public const string F = "Женский";
    public const string TG = "Трансгендер";
}


# Code\Constants\StructIdsCodes.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public static class StructIdsCodes
    {
        public const int AddEquipment = 5034;

        /// <summary>
        /// Аварийный комиссар: выезд на любое ДТП.
        /// </summary>
        public const int AvarcomAccident = 5013;

        /// <summary>
        /// Аварийный комиссар: выезд на любое страховое событие.
        /// </summary>
        public const int AvarcomAny = 5551;

        /// <summary>
        /// Аварийный комиссар: сбор справок.
        /// </summary>
        public const int AvarcomNote = 5014;

        /// <summary>
        /// Аварийный комиссар: выезд на ДТП с двумя и более участниками или пострадавшими по жизни и здоровью.
        /// </summary>
        public const int Avarcom2Par = 5012;

        /// <summary>
        /// Угон/хищение.
        /// </summary>
        public const int CarjackingCode = 5031;

        /// <summary>
        /// Ущерб.
        /// </summary>
        public const int Damage = 5016;

        /// <summary>
        /// Ремонт на СТОА дилера по направлению Страховщика.
        /// </summary>
        public const int DealerTechcenter = 5485;

        /// <summary>
        /// Эвакуация.
        /// </summary>
        public const int Evacuation = 5004;

        public const int FranchiseCauser = 5027;

        public const int FranchiseSecondIncEvent = 5028;

        /// <summary>
        /// GAP (страхование на полную стоимость).
        /// </summary>
        public const int Gap = 5036;

        /// <summary>
        /// Стекла без справок 1 раз в год.
        /// </summary>
        public const int Glass1T = 5018;

        /// <summary>
        /// Стекла без справок без ограничений.
        /// </summary>
        public const int GlassMany = 5019;

        public const int InstallmentPaymentsCode = 5096;

        /// <summary>
        /// Ремонт на СТОА по выбору Страхователя.
        /// </summary>
        public const int InsurantTechcenter = 5486;

        public const int Milelimit = 5023;

        public const int OneTimePaymentCode = 5097;

        /// <summary>
        /// Выплата без справок по кузовным деталям до 3% от страховой суммы один раз в год (BODY3P1T).
        /// </summary>
        public const int Percent3 = 5020;

        /// <summary>
        /// Выплата без справок по кузовным деталям до 5% от страховой суммы два раза в год (BODY5P2T).
        /// </summary>
        public const int Percent5 = 5021;

        public const int ProductKasko = 3042;

        /// <summary>
        /// Аренда автомобиля.
        /// </summary>
        public const int Rent = 5011;

        /// <summary>
        /// Такси.
        /// </summary>
        public const int Taxi = 5005;

        /// <summary>
        /// Ремонт на СТОА (кроме дилеров) по направлению Страховщика.
        /// </summary>
        public const int Techcenter = 5484;

        /// <summary>
        /// Техпомощь.
        /// </summary>
        public const int TechHelp = 5006;

        /// <summary>
        /// Ущерб только на условиях "Полная гибель".
        /// </summary>
        public const int TotalLoss = 5029;
    }
}


# Code\Constants\UrmApprovalThemeProlongationCodes.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public static class UrmApprovalThemeProlongationCodes
    {
        public const string ChangeOwner = "changeOwner";
        public const string ChangeInsurant = "changeInsurant";
        public const string Other = "other";
    }
}


# Code\Constants\UserGroupConstant.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    public class UserGroupConstant
    {
        public const string Nb_grs = "НБ ГРС";
        public const string Nb_intouch = "НБ Интач";
        public const string Nb_grs_supervizor = "НБ ГРС Супервайзер";
        public const string Nb_intouch_supervizor = "НБ Интач Супервайзер";
        public const string CallCenter = "CallCenter";
        public const string Delivery = "Delivery";
        public const string Clients = "Clients";
        public const string URM = "URM";
        public const string DIUnderwriter = "DIUnderwriter";
        public const string Link2Agents = "Link2Agents";
        public const string Administrators = "Administrators";
        public const string COK = "COK";
        public const string HelpDesk = "HelpDesk";
    }
}


# Code\Constants\UserModeCodes.cs
﻿namespace iRIS.KaskoLink2.Code.Constants
{
    internal class UserModeCodes
    {
        public const string CallCenter = "CallCenter";
        public const string Supervisor = "Supervisor";
    }
}


# Code\Constants\ConstantsHelpers\SASApprovalStatusCodeHelper.cs
﻿using iRIS.InsuranceCore.Entities.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace iRIS.KaskoLink2.Code.Constants.ConstantsHelpers
{
    public static class SASApprovalStatusCodeHelper
    {
        public static string GetSASStatusCode(string kaskoSasCheckProcessConstant)
        {
            switch (kaskoSasCheckProcessConstant)
            {
                case KaskoSasCheckProcessConstants.KaskoSasCheckAccept:
                    return SASApprovalStatusCodes.Accepted;
                case KaskoSasCheckProcessConstants.KaskoSasCheckUrm:
                    return SASApprovalStatusCodes.Urm;
                case KaskoSasCheckProcessConstants.KaskoSasCheckNoDecision:
                    return SASApprovalStatusCodes.NoDecision;
                case KaskoSasCheckProcessConstants.KaskoSasCheckReject:
                default:
                    return SASApprovalStatusCodes.Rejected;
            }
        }
    }
}


# Code\Constructor\ConstructorModule.cs
﻿using Autofac;
using iRIS.InsuranceCore.Code.Constructor;
using iRIS.KaskoCore.Code.Implementations;
using iRIS.KaskoCore.Code.Interfaces;
using iRIS.KaskoLink2.Code.Constructor.Implementations;
using iRIS.KaskoLink2.Code.Constructor.Implementations.CallCenterConstructor;
using iRIS.KaskoLink2.Code.Constructor.Implementations.ClientConstructor;
using iRIS.KaskoLink2.Code.Constructor.Interfaces;
using iRIS.KaskoLink2.ViewModels;

namespace iRIS.KaskoLink2.Code.Constructor
{
    public class ConstructorModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<ConstructorOfferGetter>().As<IConstructorOfferGetter>();
            builder.RegisterType<ConstructorCacheWrapper>().As<IConstructorCacheWrapper>();
            builder.RegisterType<ToggleConditionHandler>().As<IToggleConditionHandler>();
            builder.RegisterType<ConstructorSiebelWrapper>().As<IConstructorSiebelWrapper>();
            builder.RegisterType<KaskoDeltaProcessor>().As<IKaskoDeltaProcessor>();
            builder.RegisterType<CallcenterConstructorDataWrapper>().As<IConstructorDataWrapper<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<ClientConstructorDataWrapper>().As<IConstructorDataWrapper<DiKaskoClientConstructorStepViewModel>>();
            builder.RegisterType<CallcenterConstructorCalculator>().As<IConstructorCalculator<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<ClientConstructorCalculator>().As<IConstructorCalculator<DiKaskoClientConstructorStepViewModel>>();
            builder.RegisterType<ConstructorDiscountsLogic>().As<IConstructorDiscountsLogic<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<CallcenterConstructorLogic>().As<IConstructorLogic<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<ClientConstructorLogic>().As<IConstructorLogic<DiKaskoClientConstructorStepViewModel>>();
            builder.RegisterType<CallcenterConstructorModelUpdater>().As<IConstructorModelUpdater<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<ClientConstructorModelUpdater>().As<IConstructorModelUpdater<DiKaskoClientConstructorStepViewModel>>();
            builder.RegisterType<CallcenterConstructorViewModelUpdater>().As<IConstructorViewModelUpdater<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<ClientConstructorViewModelUpdater>().As<IConstructorViewModelUpdater<DiKaskoClientConstructorStepViewModel>>();
            builder.RegisterType<ConstructorDiscountModelUpdater>().As<IConstructorDiscountModelUpdater<DiKaskoConstructorStepViewModel>>();
            builder.RegisterType<KaskoDiscountApplier>().As<IDiscountApplier>().SingleInstance();
        }
    }
}


# Code\Constructor\KaskoConstructorOfferGetter.cs
﻿using iRIS.Framework.Cqrs.Interfaces;
using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.InsuranceCore.Code.ExternalFunctions;
using iRIS.InsuranceCore.Code.Queries;
using iRIS.InsuranceCore.Entities;
using iRIS.InsuranceCore.Entities.EntityExtensions;
using iRIS.KaskoCore.Code.Constants;
using iRIS.KaskoCore.Code.Extensions;
using iRIS.KaskoLink2.Code.Helpers;
using Serilog;
using System;

namespace iRIS.KaskoLink2.Code.Constructor
{
    public interface IKaskoConstructorOfferGetter
    {
        int GetConstructorOfferId(Basket basket, string loginName = null);
    }

    public class KaskoConstructorOfferGetter : IKaskoConstructorOfferGetter
    {
        public static int[] ConstructorOffers { get; set; } = new int[] 
        {
            DiKaskoOfferIds.Link2Constructor,
            DiKaskoOfferIds.Constructor,
            B2BKaskoOfferIds.KaskoKKP,
            B2BKaskoOfferIds.KaskoKKPProlongation
        };

        private readonly Link2FunctionsInvoker _invoker;
        private readonly ILogger _logger;
        private readonly IEntitySettingsManager _entitySettingsManager;
        private readonly ICqrsMediator _mediator;

        public KaskoConstructorOfferGetter(Link2FunctionsInvoker invoker, ILogger logger, IEntitySettingsManager entitySettingsManager, ICqrsMediator mediator)
        {
            _invoker = invoker;
            _logger = logger;
            _entitySettingsManager = entitySettingsManager;
            _mediator = mediator;
        }

        public int GetConstructorOfferId(Basket basket, string loginName = null)
        {
            int selectedConstructorOfferId;
            string intLoginName = loginName ?? GetLoginName(basket);

            try
            {
                if (_invoker.BasketCreatorIsLink2PartnerOrDiAgent(basket))
                {
                    if (CheckIsB2CPartnerHelper.CheckIsB2CPartner(_entitySettingsManager, intLoginName))
                    {
                        selectedConstructorOfferId = DiKaskoOfferIds.Link2Constructor;
                    }
                    else
                    {
                        selectedConstructorOfferId = basket.HasProlongationInBasket()
                            ? B2BKaskoOfferIds.KaskoKKPProlongation
                            : B2BKaskoOfferIds.KaskoKKP;
                    }
                }
                else
                {
                    selectedConstructorOfferId = DiKaskoOfferIds.Constructor;
                }
            }
            catch (Exception e)
            {
                _logger.Error(e, "Ошибка получения оффера для конструктора Link2");
                selectedConstructorOfferId = 0;
            }

            return selectedConstructorOfferId;
        }

        private string GetLoginName(Basket basket)
        {
            var basketCreator = _mediator.ResolveQuery<IQueryBasketCreator>().GetBasketCreatorDetailed(basket.EntityId, basket.Number, null);
            var integrationLogin = _mediator.ResolveQuery<IQueryIntegrationLogin>().GetIntegrationLoginByUserId(basketCreator.User.Id);
            
            return integrationLogin.IntegrationLoginName;
        }
    }
}
# Code\AutoProlongation\Implementations\KaskoAutoProlongationLogMessageTemplateGetter.cs
﻿using iRIS.KaskoCore.AutoProlongation.Code.Interfaces;
using iRIS.KaskoCore.AutoProlongation.Code.Model;

namespace iRIS.KaskoLink2.Code.AutoProlongation.Implementations
{
    internal class KaskoAutoProlongationLogMessageTemplateGetter : IKaskoAutoProlongationLogMessageTemplateGetter
    {
        private const string _prolongationErrorTemplate = "Пролонгация полиса завершилась с ошибкой.";
        private const string _kaskoBasedOnEosagoErrorTemplate = "Создание лида завершилось с ошибкой.";
        private const string _prolongationSuccessTemplate = "Пролонгация полиса завершилась успешно.";
        private const string _kaskoBasedOnEosagoSuccessTemplate = "Создание лида завершилось успешно.";
        private const string _kaskoUpsalesToOssSuccessTemplate = "Отправка полиса напрямую в ОСС завершилась успешно";
        private const string _kaskoUpsalesToOssErrorTemplate = "Отправка полиса напрямую в ОСС завершилась с ошибкой";
        private const string _kaskoRopTemplate = "Создание ROP активности завершилась успешно";
        private const string _kaskoRopErrorTemplate = "Создание ROP активности завершилась с ошибкой";

        public string GetMessageTemplate(KaskoAutoProlongationMessage autoProlongationMessage, bool isError)
        {
            return autoProlongationMessage switch
            {
                { IsKaskoBasedOnEosago: true } when isError == true => _kaskoBasedOnEosagoErrorTemplate,
                { IsKaskoBasedOnEosago: true } when isError == false => _kaskoBasedOnEosagoSuccessTemplate,
                { IsKaskoUpsalesToOss: true } when isError == true => _kaskoUpsalesToOssErrorTemplate,
                { IsKaskoUpsalesToOss: true } when isError == false => _kaskoUpsalesToOssSuccessTemplate,
                { IsRop: true } when isError == true => _kaskoRopErrorTemplate,
                { IsRop: true } when isError == false => _kaskoRopTemplate,
                { IsKaskoBasedOnEosago: false } when isError == true => _prolongationErrorTemplate,
                { IsKaskoBasedOnEosago: false } when isError == false => _prolongationSuccessTemplate
            };
        }
    }
}


# Code\AutoProlongation\Implementations\KaskoAutoProlongationPackageGetter.cs
﻿using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.InsuranceCore.OSS.Code.ClientWrappers.GetPolicyProlongation;
using iRIS.InsuranceCore.OSS.Code.ClientWrappers.GetPolicyProlongation.RequestResponse;
using iRIS.KaskoCore.AutoProlongation.Code.Implementations;
using iRIS.KaskoCore.AutoProlongation.Code.Model;
using iRIS.KaskoLink2.Code.Queries;
using iRIS.KaskoLink2.Code.TypeExtensions;
using iRIS.KaskoLink2.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace iRIS.KaskoLink2.Code.AutoProlongation.Implementations
{
    /// <inheritdoc />
    internal class KaskoAutoProlongationPackageGetter : BaseKaskoAutoProlongationPackageGetter
    {
        private readonly IQueryOsagoProlongationClientType _queryOsagoProlongationClientType;
        private IReadOnlyCollection<string> _osagoFilterChannelCodes = new[] { "SRL08009", "SRL00777", "SRL08005", "SRL08004" };

        /// <summary>
        /// Initializes a new instance of the <see cref="KaskoAutoProlongationPackageGetter"/> class.
        /// </summary>
        /// <param name="ossGetPolicyProlongation">Осс врапер пролангационных полисов.</param>
        /// <param name="entitySettingsManager">Менедженр настроек пролонгации.</param>
        /// <param name="queryOsagoProlongationClientType">Менеджер данных сегментирования пролонгации чистого ОСАГО</param>
        public KaskoAutoProlongationPackageGetter(
            IOssGetPolicyProlongationWrapper ossGetPolicyProlongation,
            IEntitySettingsManager entitySettingsManager,
            IQueryOsagoProlongationClientType queryOsagoProlongationClientType)
            : base(ossGetPolicyProlongation, entitySettingsManager)
        {
            _queryOsagoProlongationClientType = queryOsagoProlongationClientType;
        }

        private OsagoProlongationClientType GetOsagoProlongationClientType(string policyNumber, DateTime presentDay)
         => _queryOsagoProlongationClientType
            .GetOsagoProlongationClientTypesByDate(presentDay)
            ?.TryGetValue(policyNumber, out var type) ?? false ? type : null;

        protected override bool GetClientTypeCode(PolicyGroupDto policy, DateTime presentDay, bool isValidFilterContext, out string clientTypeCode)
        {
            var osagoProlongationClientType = isValidFilterContext && policy.IsClearOsaga() ? GetOsagoProlongationClientType(policy.Policies.FirstOrDefault().PolicyNumber, presentDay) : null;

            clientTypeCode = null;

            if (isValidFilterContext && policy.IsClearOsaga() && osagoProlongationClientType == null)
            {
                return false;
            }

            clientTypeCode = osagoProlongationClientType?.ClientTypeCode;

            return true;
        }

        protected override bool IsValidFilterContext(KaskoAutoProlongationPrepareSetting setting, DateTime presentDay)
        {
            return _osagoFilterChannelCodes.Any(code => setting.ChannelCodes?.Contains(code) ?? false) && presentDay <= _queryOsagoProlongationClientType.GetLastDate();
        }
    }
}   


# Code\AutoProlongation\Implementations\KaskoAutoProlongationPackageReport.cs
﻿using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.InsuranceCore;
using iRIS.InsuranceCore.Entities;
using Serilog;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using iRIS.KaskoLink2.Code.AutoProlongation.Constants;
using iRIS.KaskoLink2.Code.AutoProlongation.Helpers;
using iRIS.KaskoCore.AutoProlongation.Code.Implementations;
using iRIS.KaskoCore.AutoProlongation.Code.Model;
using iRIS.KaskoCore.AutoProlongation.Code.Interfaces;
using EmailClient.Code.Interfaces;
using iRIS.KaskoCore.Code.Helpers;

namespace iRIS.KaskoLink2.Code.AutoProlongation.Implementations
{
    internal class KaskoAutoProlongationPackageReport : BaseKaskoAutoProlongationPackageReport, IKaskoAutoProlongationPackageReport
    {
        public KaskoAutoProlongationPackageReport(
            IEmailClient emailClient,
            IEntitySettingsManager entitySettingsManager,
            IOptions<InsuranceCoreModuleConfiguration> configuration,
            ILogger logger)
            : base(emailClient, entitySettingsManager, configuration, logger)
        {
        }

        protected override string ErrorMessage => "Ошибка отправки отчета по автопролонгации.";

        public async Task SendPackageReportAsync(AutoProlongationPackage package)
        {
            await base.SendPackageReportAsync(package);
        }

        protected override string BuildMailBody(AutoProlongationPackage package)
        {
            return BuildMailHelper.BuildMailBody(package, BuildMailHelper.CreateTable);
        }

        protected override string BuildMailSubject(AutoProlongationPackage package, InsuranceCoreModuleConfiguration configuration)
        {
            return $"[{KaskoReportHelper.GetEnvironment(configuration)}_IRIS] Завершение процесса автопролонгации договоров {package.Description}.";
        }

        protected override KaskoAutoProlongationReportSetting GetReportSetting(IEntitySettingsManager entitySettingsManager)
        {
           return entitySettingsManager.GetSettingValue<KaskoAutoProlongationReportSetting>(ReportSetting.KaskoAutoProlongationReportSetting);
        }
    }
}


# Code\AutoProlongation\Implementations\KaskoAutoProlongationPartnerPackageReport.cs
﻿using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.InsuranceCore;
using iRIS.InsuranceCore.Entities;
using iRIS.KaskoLink2.Code.AutoProlongation.Interfaces;
using Serilog;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using iRIS.KaskoLink2.Code.AutoProlongation.Constants;
using iRIS.KaskoLink2.Code.AutoProlongation.Helpers;
using iRIS.KaskoCore.AutoProlongation.Code.Implementations;
using iRIS.KaskoCore.AutoProlongation.Code.Model;
using EmailClient.Code.Interfaces;
using iRIS.KaskoCore.Code.Helpers;

namespace iRIS.KaskoLink2.Code.AutoProlongation.Implementations
{
    internal class KaskoAutoProlongationPartnerPackageReport : BaseKaskoAutoProlongationPackageReport, IKaskoProlongationPartnerPackageReport
    {
        public KaskoAutoProlongationPartnerPackageReport(
            IEmailClient emailClient,
            IEntitySettingsManager entitySettingsManager,
            IOptions<InsuranceCoreModuleConfiguration> configuration,
            ILogger logger)
            : base(emailClient, entitySettingsManager, configuration, logger)
        {
        }

        protected override string ErrorMessage => "Ошибка отправки отчета по автопролонгации партнерских полисов.";

        public async Task SendPackageReportAsync(AutoProlongationPackage package)
        {
            await base.SendPackageReportAsync(package);
        }

        protected override string BuildMailBody(AutoProlongationPackage package)
        {
            return BuildMailHelper.BuildMailBody(package, BuildMailHelper.CreateTable);
        }

        protected override string BuildMailSubject(AutoProlongationPackage package, InsuranceCoreModuleConfiguration configuration)
        {
            return $"[{KaskoReportHelper.GetEnvironment(configuration)}_IRIS] Завершение процесса автопролонгации договоров {package.Description}.";
        }

        protected override KaskoAutoProlongationReportSetting GetReportSetting(IEntitySettingsManager entitySettingsManager)
        {
           return entitySettingsManager.GetSettingValue<KaskoAutoProlongationReportSetting>(ReportSetting.KaskoProlongationPartnerReportSetting);
        }
    }
}


# Code\AutoProlongation\Implementations\KaskoAutoProlongationReportGenerator.cs
﻿using iRIS.InsuranceCore.Entities;
using iRIS.KaskoCore.AutoProlongation.Code.Interfaces;
using iRIS.KaskoCore.AutoProlongation.Code.Model;
using iRIS.KaskoCore.Code.Helpers;
using iRIS.KaskoLink2.Code.AutoProlongation.Interfaces;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace iRIS.KaskoLink2.Code.AutoProlongation.Implementations
{
    internal class KaskoAutoProlongationReportGenerator : IKaskoAutoProlongationReportGenerator
    {
        private readonly IKaskoAutoProlongationPackageReport _packageReport;
        private readonly IKaskoAutoProlongationDataReport _dataReport;
        private readonly IKaskoSubscriptionPackageReport _kaskoSubscriptionPackageReport;
        private readonly IKaskoBasedOnEosagoPackageReport _kaskoBasedOnEosagoPackageReport;
        private readonly IKaskoProlongationPartnerPackageReport _kaskoProlongationPartnerPackageReport;
        private readonly IKaskoUpsalesToOssPackageReport _kaskoUpsalesToOssPackageReport;
        private readonly IKaskoRopPackageReport _kaskoRopPackageReport;
        private readonly IOsagoSegmentationExpireNotifier _osagoSegmentationExpireNotifier;

        public KaskoAutoProlongationReportGenerator(
            IKaskoAutoProlongationPackageReport packageReport,
            IKaskoAutoProlongationDataReport prevDataReport,
            IKaskoSubscriptionPackageReport kaskoSubscriptionPackageReport,
            IKaskoBasedOnEosagoPackageReport kaskoBasedOnEosagoPackageReport,
            IKaskoProlongationPartnerPackageReport kaskoProlongationPartnerPackageReport,
            IKaskoUpsalesToOssPackageReport kaskoUpsalesToOssPackageReport,
            IKaskoRopPackageReport kaskoRopPackageReport,
            IOsagoSegmentationExpireNotifier osagoSegmentationExpireNotifier)
        {
            _packageReport = packageReport ?? throw new ArgumentNullException(nameof(_packageReport));
            _dataReport = prevDataReport ?? throw new ArgumentNullException(nameof(_dataReport));
            _kaskoSubscriptionPackageReport = kaskoSubscriptionPackageReport ?? throw new ArgumentNullException(nameof(_kaskoSubscriptionPackageReport));
            _kaskoBasedOnEosagoPackageReport = kaskoBasedOnEosagoPackageReport ?? throw new ArgumentNullException(nameof(_kaskoBasedOnEosagoPackageReport));
            _kaskoProlongationPartnerPackageReport = kaskoProlongationPartnerPackageReport ?? throw new ArgumentNullException(nameof(_kaskoProlongationPartnerPackageReport));
            _kaskoUpsalesToOssPackageReport = kaskoUpsalesToOssPackageReport ?? throw new ArgumentNullException(nameof(_kaskoUpsalesToOssPackageReport));
            _kaskoRopPackageReport = kaskoRopPackageReport ?? throw new ArgumentNullException(nameof(_kaskoRopPackageReport));
            _osagoSegmentationExpireNotifier = osagoSegmentationExpireNotifier ?? throw new ArgumentNullException(nameof(_osagoSegmentationExpireNotifier));
        }

        public async Task GenerateReportAsync(AutoProlongationPackage package)
        {
            var kaskoAutoProlongationMessage = GetKaskoAutoProlongationMessage(package);
            if (IsSubscription(kaskoAutoProlongationMessage))
            {
                await _kaskoSubscriptionPackageReport.SendPackageReportAsync(package);
            }
            else if (IsKaskoBasedOnEosago(kaskoAutoProlongationMessage))
            {
                await _kaskoBasedOnEosagoPackageReport.SendPackageReportAsync(package);
            }
            else if (IsProlongationPartner(kaskoAutoProlongationMessage))
            {
                await _kaskoProlongationPartnerPackageReport.SendPackageReportAsync(package);
            }
            else if (IsKaskoUpsalesToOss(kaskoAutoProlongationMessage))
            {
                await _kaskoUpsalesToOssPackageReport.SendPackageReportAsync(package);
            }
            else if (IsKaskoRop(kaskoAutoProlongationMessage))
            {
                await _kaskoRopPackageReport.SendPackageReportAsync(package);
            }
            else
            {
                var packageReportTask = _packageReport.SendPackageReportAsync(package);
                var dataReportTask = _dataReport.SendDataReportAsync(package);
                var segmentationExpireNotifierTask = _osagoSegmentationExpireNotifier.SendNotificationAsync();

                await Task.WhenAll(packageReportTask, dataReportTask, segmentationExpireNotifierTask);
            }
        }

        private bool IsSubscription(KaskoAutoProlongationMessage kaskoAutoProlongationMessage)
        {
            return kaskoAutoProlongationMessage?.IsSubscription == true;
        }

        private bool IsKaskoBasedOnEosago(KaskoAutoProlongationMessage kaskoAutoProlongationMessage)
        {
            return kaskoAutoProlongationMessage?.IsKaskoBasedOnEosago == true;
        }

        private bool IsProlongationPartner(KaskoAutoProlongationMessage kaskoAutoProlongationMessage)
        {
            return kaskoAutoProlongationMessage?.IsProlongationPartner == true;
        }

        private bool IsKaskoUpsalesToOss(KaskoAutoProlongationMessage kaskoAutoProlongationMessage)
        {
            return kaskoAutoProlongationMessage?.IsKaskoUpsalesToOss == true;
        }

        private bool IsKaskoRop(KaskoAutoProlongationMessage kaskoAutoProlongationMessage)
        {
            return kaskoAutoProlongationMessage?.IsRop == true;
        }

        private KaskoAutoProlongationMessage GetKaskoAutoProlongationMessage(AutoProlongationPackage package)
        {
            if (package.AutoProlongationPolicies.Any())
            {
                var autoProlongationMessage = KaskoAutoProlongationHelper.GetKaskoAutoProlongationMessage(package.AutoProlongationPolicies.First().Message);
                return autoProlongationMessage;
            }

            return null;
        }
    }
}
sb.Append(" являются неотъемлемой частью настоящего Полиса. Принимая настоящий Полис и оплачивая страховую премию, Страхователь подтверждает, что Правила получил, ознакомлен и согласен с их условиями, а также с условиями настоящего Полиса и его приложений в полном объеме. Дополнительно с Правилами страхования можно ознакомиться на официальном сайте Страховщика renins.ru.");
            sb.Append(Environment.NewLine);
            sb.Append("Принимая настоящий полис и оплачивая страховую премию, Страхователь подтверждает, что все сведения, указанные в настоящем Полисе и в его приложениях, являются полными и достоверными. Стороны пришли к соглашению, что настоящий Полис и любые документы в рамках настоящего Полиса могут быть подписаны Страховщиком с использованием факсимиле или электронной подписи уполномоченного представителя Страховщика. В случае разночтений между условиями настоящего Полиса и Правилами приоритет будут иметь условия настоящего Полиса.");
            sb.Append(Environment.NewLine);
            sb.Append("Принимая настоящий полис и оплачивая страховую премию, Страхователь подтверждает, что он не является иностранными публичным должностным лицом и / или его родственником, должностным лицом публичной международной организации, а также лицом, замещающим(занимающим) государственную должность РФ, должность члена Совета Директоров ЦБ РФ, должность федеральной государственной службы, назначение и освобождение от которой осуществляется Президентом РФ или Правительством РФ, должность в ЦБ РФ, государственных корпорациях и иных организациях, созданных РФ на основании федеральных законов, включенную в перечни должностей, определяемые Президентом РФ.В ином случае Страхователь обязуется предоставить документы, подтверждающие должность / наименование и адрес работодателя / степень родства / статус(супруг или супруга).Также Страхователь подтверждает, что не имеет регистрации, места жительства / нахождения, счета в банке в государстве(на территории), которое(-ая) не выполняет рекомендации ФАТФ.Страхователь подтверждает, что не ограничен в дееспособности, а также его действия по иным основаниям не контролирует иное физическое лицо(бенефициарный владелец).");
            sb.Append(Environment.NewLine);
            sb.Append("Принимая настоящий Полис и оплачивая страховую премию Страхователь выражает Страховщику согласие на обработку персональных данных физических лиц, содержащихся в настоящем Полисе и в иных документах, передаваемых Страховщику для исполнения обязательств по данному Полису, а также в целях продвижения товаров, работ, услуг на рынке путем осуществления Страховщиком прямых контактов с помощью связи.");
            sb.Append(Environment.NewLine);
            sb.Append("Принимая настоящий Полис и оплачивая страховую премию Страхователь подтверждает, что в полном объеме получил информацию, предусмотренную пунктом 2.1.2. Базового стандарта защиты прав и интересов физических и юридических лиц — получателей финансовых услуг, оказываемых членами саморегулируемых организаций, объединяющих страховые организации, утв. Решением КФН ЦБ РФ, Протокол от 09.08.18 № КФНП-24. Принимая настоящий Полис и оплачивая страховую премию Страхователь выражает согласие осуществлять взаимодействие со Страховщиком путем обращения в офисы Страховщика, посредствам почтовой и телефонной связи, а также иными способами в случаях, специально предусмотренных Правилами страхования. Принимая настоящий Полис и оплачивая страховую премию Страхователь выражает согласие получать сообщения и уведомления от Страховщика, в том числе в электронной форме, по адресам, телефонам, указанным в настоящем Полисе. Информация об адресах офисов Страховщика, в которых осуществляется прием документов при наступлении страховых случаев и иных обращений, а также о случаях и порядке приема документов в электронной форме, размещена на официальном сайте Страховщика и может быть получена при обращении по телефонам Страховщика, указанным в настоящем Полисе.");
            return sb.ToString();
        }

        public List<PsoAdditionalFieldModel> CreatePsoAdditionalFields(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var auto = deal.GetInsObjectAuto();

            var result = new List<PsoAdditionalFieldModel>
            {
                CreatePsoAdditionalFieldValueModel("policy_number", deal.PolicyNumber),
                CreatePsoAdditionalFieldValueModel("marka_model", string.Join(", ", auto.AutoBrandName, auto.AutoModelName)),
                CreatePsoAdditionalFieldValueModel("VIN", auto.Vin),
                CreatePsoAdditionalFieldValueModel("rate_num", basket.Number),
                CreatePsoAdditionalFieldValueModel("GRZ", auto.RegistrationNumber)
            };

            return result.Where(x => x != null).ToList();
        }

        public bool ReadConfigBooleanVariable(string variableName)
        {
            var configService = ServiceProvider.GetRequiredService<IConfigurationRoot>();
            return configService.GetValue<bool>(variableName);
        }

        public string ReadConfigStringVariable(string variableName)
        {
            var configService = ServiceProvider.GetRequiredService<IConfigurationRoot>();
            return configService.GetValue<string>(variableName);
        }

        public string GetUrlResumeProcessKaskoWithToken(AuthorizationData authorizationData, Basket basket)
        {
            return GetUrlResumeProcessKaskoWithToken(authorizationData, basket, false);
        }

        public string GetUrlResumeProcessKaskoWithToken(AuthorizationData authorizationData, Basket basket, bool isReusable)
        {
            // Из фрейма на сайте интача не получается сделать редирект из процесса оплаты обратно в процесс Каско
            if (basket.IsIntouchWeb())
            {
                return null;
            }

            var resumeProcessUrlGetter = ServiceProvider.GetRequiredService<IKaskoResumeProcessUrlGetter>();

            return resumeProcessUrlGetter.GetUrlResumeProcessKaskoWithToken(authorizationData.CurrentUserId, authorizationData.BrowserSessionId, basket, isReusable);
        }

        public void SelectConstructorProposal(Basket basket)
        {
            IKaskoConstructorOfferGetter constructorOfferGetter = ServiceProvider.GetService<IKaskoConstructorOfferGetter>();

            var deal = basket.GetKaskoDeal();
            var proposal = deal.Proposals.Single(p => p.Offer_Id == constructorOfferGetter.GetConstructorOfferId(basket));

            deal.SetSelectedProposal(proposal);
        }

        /// <summary>
        /// Блок по пересчету со скидкой. Копипаста из DiKaskoConstructorExtension.
        /// </summary>
        public async Task ApplyDiscount(Basket basket)
        {
            var discountCalculationHelper = ServiceProvider.GetRequiredService<DiscountCalculationHelper>();
            var calcData = discountCalculationHelper.GetCalcData(basket);

            var discountApplier = ServiceProvider.GetRequiredService<IDiscountApplier>();
            await discountApplier.CalculteWithAppliedDiscountsConsideringCombinationStatusAsync(calcData);
        }

        public void ApplyCostCenterByPromocode(Basket basket)
        {
            if (basket.PromoCode == null)
            {
                return;
            }

            var discountApplier = ServiceProvider.GetRequiredService<IDiscountApplier>();
            var promocodeManager = ServiceProvider.GetRequiredService<IPromocodeManager>();

            if (!discountApplier.IsPromoCodeAppliedAtKaskoDeal(basket))
            {
                return;
            }

            foreach (var deal in basket.Deals)
            {
                //только сделки каско
                if (deal.Product.ProductDictCode != ProductDictCodes.Kasko)
                {
                    continue;
                }

                var promocode = promocodeManager.GetActualPromocode(basket.PromoCode.Id, deal.Product.Id);

                //промкод протух
                if (promocode == null)
                {
                    continue;
                }

                discountApplier.SetDealCostCenterCodeByPromocode(deal, promocode);
            }
        }

        public bool ApplyPromocodeToDiscounts(Basket basket, Dictionary<string, string> inputModelContext)
        {
            var promocodeValue = string.Empty;
            if (inputModelContext != null && inputModelContext.ContainsKey("promocode"))
            {
                promocodeValue = inputModelContext["promocode"];
            }

            var result = ApplyPromo(basket, promocodeValue);

            // если пользователь сбросил промокод и промокод пришёл из url, то очищаем значение, принятое из url
            if (string.IsNullOrEmpty(promocodeValue) && !string.IsNullOrEmpty(basket.ExternalTagList?.PromoCode))
            {
                basket.ExternalTagList.PromoCode = string.Empty;
            }

            return result;
        }

        public bool ApplyPromocodeForLink2(Basket basket)
        {
            var kaskoDeal = basket.GetKaskoDeal();
            var promocode = kaskoDeal?.PromoCode;
            var promocodeValue = promocode?.Code;
            if (string.IsNullOrEmpty(promocodeValue))
            {
                return false;
            }

            var result = ApplyPromo(basket, promocodeValue);

            return result;
        }

        public void CreateEmptyCourierDealIntermediary(Basket basket)
        {
            var dealsWithCourierDelivery = basket.Deals.Where(x =>
                x.IsInBasket == true
                && x.IsExternal != true
                && x.Deliveries.Any(del => del.DeliveryWayCode == DeliveryWayCodes.ByCourier));

            foreach (var deal in dealsWithCourierDelivery)
            {
                var courierDealIntermediary = deal.DealIntermediaries
                    .Where(x => x.InteractionTypeCode == InteractionTypeEnum.Courier.ToString()
                                || x.Intermediary?.InteractionTypeCode == InteractionTypeEnum.Courier.ToString())
                    .FirstOrDefault();

                if (courierDealIntermediary == null)
                {
                    var courierIntermediary = new Intermediary
                    {
                        InteractionTypeCode = InteractionTypeEnum.Courier.ToString(),
                        IntermediaryStatusCode = "IntActive",
                    };
                    courierIntermediary.EntityContext[KaskoCore.Siebel.Code.Constants.EntityContextKeys.PositionTypeKey] = KaskoCore.Siebel.Code.Constants.SiebelPositionTypeNames.DeliveryDepartment;
                    deal.DealIntermediaries.Add(new DealIntermediary
                    {
                        InteractionTypeCode = InteractionTypeEnum.Courier.ToString(),
                        Intermediary = courierIntermediary
                    });
                }
            }
        }

        private bool ApplyPromo(Basket basket, string promo)
        {
            var kaskoDeal = basket.GetKaskoDeal();
            if (kaskoDeal == null)
            {
                return false;
            }

            if (string.IsNullOrWhiteSpace(promo))
            {
                ResetPromocode(basket);
                return true;
            }

            var promoCode = GetBestAvailablePromocode(promo, kaskoDeal);

            if (promoCode == null || !string.IsNullOrEmpty(promoCode.CallCenterName))
            {
                ResetPromocode(basket);

                return false;
            }

            basket.PromoCode = promoCode;

            var discountApplier = ServiceProvider.GetRequiredService<IDiscountApplier>();

            discountApplier.ApplyPromocode(kaskoDeal, promoCode, basket.ExternalTagList.UseDrafts);

            if (discountApplier.IsPromoCodeAppliedAtKaskoDeal(basket))
            {
                discountApplier.SetDealCostCenterCodeByPromocode(kaskoDeal, promoCode);
                var osagodeal = basket.GetOsagoDealInBasket();
                if (osagodeal != null)
                {
                    discountApplier.SetDealCostCenterCodeByPromocode(osagodeal, promoCode);
                }
            }

            return true;
        }

        public async Task CalculateProposals(Basket basket)
        {
            await CalculateProposalsInternalAsync(basket, DiscountCalculationStrategy.CalculateForAgreedOrApproveNotRequiredDiscounts);
        }

        private async Task CalculateProposalsInternalAsync(Basket basket, DiscountCalculationStrategy? discountCalculationStrategy = null)
        {
            var kaskoCalculationWrapper = ServiceProvider.GetRequiredService<IKaskoCalculationWrapper>();
            await kaskoCalculationWrapper.CalculateAndApplyProposalsAsync(basket, discountCalculationStrategy, true);
        }

        public async Task CalculateProposalsForLink2WithPromocode(Basket basket)
        {
            var kaskoRater = ServiceProvider.GetRequiredService<IKaskoRater>();
            await kaskoRater.CalculateDealAsync(basket, basket.GetKaskoDeal());

            basket.UpdatePrice();
        }

        public void ActivatePromocode(Basket basket)
        {
            var kaskoDeal = basket.GetKaskoDeal();
            if (kaskoDeal != null &&
                basket.PromoCode != null &&
                basket.PromoCode.Code.Contains(ReninsPlusConstants.ReninsPlusPromocodePrefix))
            {
                var code = basket.PromoCode.Code.Replace(ReninsPlusConstants.ReninsPlusPromocodePrefix, string.Empty);
                var reninsPlusPromocodeService = ServiceProvider.GetRequiredService<IReninsPlusPromocodeService>();
                reninsPlusPromocodeService.ActivatePromocode(
                    code, kaskoDeal.PolicyNumber, PolicyType.Kasko);
            }
        }

        public void CorrectDeliveryDate(Basket basket, bool isProcessApiRequest)
        {
            if (isProcessApiRequest && basket.BasketStatusCode == "Registration")
            {
                var kaskoDeal = basket.GetKaskoDeal();

                var delivery = kaskoDeal?.GetLastDelivery();

                if (delivery?.Date?.Date <= DT.Now.Date && delivery?.DeliveryWayCode == DeliveryWayCodes.Email)
                {
                    delivery.Date = DT.Now.AddDays(1);
                }
            }
        }

        public void ApplyDealsConditions(List<Deal> deals, List<DealConditions> dealsConditions)
        {
            var kaskoDealManager = ServiceProvider.GetRequiredService<IDiKaskoDealManager>();
            kaskoDealManager.ApplyDealsCondition(deals, dealsConditions.Convert());
        }

        private void ResetPromocode(Basket basket)
        {
            var kaskoDeal = basket.Deals.SingleOrDefault(d => d.Product.ProductDictCode == ProductDictCodes.Kasko);
            if (kaskoDeal == null)
            {
                return;
            }

            //clear from discount
            var discountApplier = ServiceProvider.GetRequiredService<IDiscountApplier>();
            discountApplier.ClearPromocode(kaskoDeal, basket.ExternalTagList.UseDrafts);
            var constructorCacheWrapper = ServiceProvider.GetRequiredService<IConstructorCacheWrapper>();
            var constructorDeal = constructorCacheWrapper.GetDealFromCache(kaskoDeal.Id);
            if (constructorDeal != null)
            {
                discountApplier.ClearPromocode(constructorDeal, basket.ExternalTagList.UseDrafts);
                constructorCacheWrapper.SaveDealInCache(constructorDeal);
            }

            if (basket.PromoCode?.Code != null
                && basket.PromoCode.Code.Contains(ReninsPlusConstants.ReninsPlusPromocodePrefix))
            {
                var reninsPlusService =
                    ServiceProvider.GetRequiredService<IReninsPlusPromocodeService>();
                var promoCode = basket.PromoCode.Code.Replace(
                    ReninsPlusConstants.ReninsPlusPromocodePrefix,
                    string.Empty);
                reninsPlusService.UnholdPromocode(promoCode, PolicyType.Kasko);
            }

            basket.PromoCode = null;
// Идем непосредственно к блоку определения выхода из подпроцесса только если выполняется одно из условий:
            // нет ни ПСО, ни ОТА
            // есть завершенная ОТА
            if ((inspection == null && !IsOtaStepAdded(basket)) || (IsOtaStepAdded(basket) && !CanEditOtaStatus(basket)))
            {
                return false;
            }

            // если не выполнено любое из указанных выше условий, переходим к форме ожидания ОТА
            return true;
        }

        /// <summary>
        /// Проверка необходимости проведения проверки ОТА (подпроцесс ОТА для линковых партнеров и подпроцесс возобновления).
        /// </summary>
        public bool CanEditOtaStatus(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                return OtaHelper.Link2PartnersAccessStatuses.Contains(approval.OtaApprovalStatusCode);
            }

            return false;
        }

        /// <summary>
        /// Проверка возможности перехода в подпроцесс ОТА/ПСО в зависимости от статуса otaApproval (подпроцесс возобновления).
        /// </summary>
        public bool IsOtaStepAccessible(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                return OtaHelper.DocReviewerAccessStatusCodes.Contains(approval.OtaApprovalStatusCode);
            }

            return false;
        }

        public bool IsOtaFinalStep(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                return OtaHelper.OtaFinalStatuses.Contains(approval.OtaApprovalStatusCode);
            }

            return false;
        }

        /// <summary>
        /// Проверка возможности перехода в подпроцесс ОТА/ПСО зависимости от inspection (подпроцесс возобновления).
        /// </summary>
        public bool IsPsoStepAccessible(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var inspection = _mediator.ResolveQuery<IQueryKaskoInspection>().GetInspectionByDealEntityId(deal.EntityId);

            return inspection != null;
        }

        public bool IsPaymentByA7Receipt(Basket basket)
        {
            return basket.GetKaskoDeal().Invoices.Any(i => i.PaymentTypeCode == PaymentTypeCodes.ByReceiptA7);
        }

        public void ProcessBamIntegrationLog(Basket basket, string errorMessage)
        {
            if (string.IsNullOrEmpty(errorMessage))
            {
                return;
            }

            basket.GetKaskoDeal().AutoDeal.BamIntegrationLog.Add(new BamIntegrationLog { ErrorMessage = errorMessage.TrimMaxLength(100) });
        }

        public void ProcessBamIntegrationResult(Basket basket, string a7number)
        {
            if (string.IsNullOrEmpty(a7number))
            {
                return;
            }

            foreach (var invoice in basket.GetKaskoDeal().Invoices)
            {
                invoice.A7Number = a7number;
            }
        }

        public bool CheckIsA7Number(Basket basket)
        {
            foreach (var invoice in basket.GetKaskoDeal().Invoices)
            {
                if (invoice.A7Number == null)
                {
                    return false;
                }
            }

            return true;
        }

        public string GetBasketRepaymentDate(Basket basket)
        {
            return basket.Payments.First().GetLetterRepaymentDate();
        }

        public void SetAutoCost(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var autoCostCalculator = _serviceProvider.GetRequiredService<IAutoCostCalculator>();
            deal.GetAuto().AutoCost = autoCostCalculator.GetAutoCostAsync(deal).ConfigureAwait(false).GetAwaiter().GetResult();
        }

        /// <summary>
        /// Если в запросе на расчет передано имя оффера, помечает выбранный Proposal как IsSelected.
        /// Повторный прерасчет сделки на данном этапе не проводится, так как произойдет на сегментации.
        /// </summary>
        /// <param name="basket">корзина.</param>
        /// <param name="selectedPacketName">имя выбранного пакета.</param>
        public void SetSelectedProposal(Basket basket, string selectedPacketName)
        {
            if (string.IsNullOrEmpty(selectedPacketName))
            {
                return;
            }

            var deal = basket.GetKaskoDeal();

            var requestedProposal = deal.Proposals
                .FirstOrDefault(p => p.Offer.Name == selectedPacketName
                || p.Offer.OfferMappings.Any(om => om.PackageName == selectedPacketName));

            if (requestedProposal != null)
            {
                deal.SetSelectedProposal(requestedProposal);
            }
        }

        /// <summary>
        /// Возвращает признак выбранного оффера коробки.
        /// </summary>
        /// <param name="basket"></param>
        /// <returns>true, если выбранное предложение является оффером-коробкой.</returns>
        public bool IsBoxProposalSelected(Basket basket)
        {
            return basket.GetKaskoDeal()?.IsBox() == true;
        }

        public string GetOtaApprovalStatusCode(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                return approval.OtaApprovalStatusCode;
            }

            return null;
        }

        public string AlertMessageDocumentsNotAttached(Basket basket)
        {
            if (basket.EntityContext.TryGetValue(nameof(PreInsuranceCheckModel), out PreInsuranceCheckModel model)
                && !string.IsNullOrEmpty(model.Comment))
            {
                if (model.Type == PreInsuranceCheckTypes.Ota)
                {
                    return string.Empty;
                }

                return model.Comment;
            }

            return "Документы не приложены";
        }

        /// <summary>
        /// Включена ли настройка NeedSendMailBeforePaymentSetting.
        /// </summary>
        /// <param name="integrationLogin"></param>
        /// <returns></returns>
        public bool CanUseNeedSendMailBeforePaymentSetting(string integrationLogin)
        {
            return _entitySettingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoOsagoEntitySettingCodes.NeedSendMailBeforePaymentSetting,
                KaskoOsagoEntitySettingCodes.NeedSendMailBeforePaymentSettingDefault,
                KaskoOsagoEntitySettingCodes.NeedSendMailBeforePaymentSettingByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                true,
                _logger);
        }

        private Discount GetUrmDiscount(Basket basket) =>
            basket.GetKaskoDeal().GetSelectedDiscountCombination().Discounts.SingleOrDefault(d =>
                d.CalculationComponentCoefficient.Coefficient.Name.Equals(DiscountCoefficientNames.K_URM, StringComparison.OrdinalIgnoreCase));

        /// <summary>
        /// Проверка на факт наличия проверки ОТА.
        /// </summary>
        private bool IsOtaStepAdded(Basket basket)
        {
            return basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out _);
        }

        /// <summary>
        /// Возвращает список кодов печатных форм, отправляемых после оплаты.
        /// </summary>
        /// <returns>Список кодов печатных форм.</returns>
        public string GetAfterPaymentPrintingFormCodes() => $"" +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_LINK2_POLICY_AUTO_KASKO}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_POLICY_KASKO_FALLING_OBJECT}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_POLICY_KASKO_DISASTER}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_POLICY_KASKO_DISASTER_AND_FALLING_OBJECT}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_EKASKO_TERMS}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_LINK2_KASKO_KID}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_KASKO_KID_NO_POLICY}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_POLICY_AUTO_DAGO}," +
            $"{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_POLICY_AUTO_KASKO_KID150}";

        /// <summary>
        /// Возвращает список кодов печатных форм, отправляемых перед оплатой.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <returns>Список кодов печатных форм.</returns>
        public string GetPrePaymentPrintingFormCodes(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var printingFormCode = GetAfterPaymentPrintingFormCodes();

            printingFormCode += deal.Invoices.Any(i => i.PaymentTypeCode == PaymentTypeCodes.PaymentInvoice)
                ? $",{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_INVOICE_AUTO_KASKO}"
                : deal.Invoices.Any(i => i.PaymentTypeCode == PaymentTypeCodes.ByReceiptSber)
                    ? $",{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_RECEIPT_SBER_AUTO_KASKO}"
                    : deal.Invoices.Any(i => i.PaymentTypeCode == PaymentTypeCodes.ByReceiptA7)
                        ? $",{KaskoOsagoPrintingFormCodes.PRINTING_FORM_CODE_RECEIPT_A7_AUTO_KASKO}"
                        : string.Empty;

            return printingFormCode;
        }

        public bool NeedGeneratePaymentLink(Basket basket)
        {
            if (basket == null)
            {
                return false;
            }

            if (!basket.Payments.Any() || string.IsNullOrEmpty(basket.Payments[0].Hash))
            {
                return true;
            }

            return false;
        }

        public void SavePaymentLink(Basket basket, string paymentLink)
        {
            if (!string.IsNullOrEmpty(paymentLink))
            {
                basket.SetPaymentLink(paymentLink);
            }
        }

        public void SaveDateRepayment(Basket basket, string dateRepayment)
        {
            if (!string.IsNullOrEmpty(dateRepayment))
            {
                basket.SetDateRepayment(dateRepayment);
            }
        }

        /// <summary>
        /// Проверка признака партнера B2C.
        /// </summary>
        /// <returns>Результат проверки.</returns>
        public bool IsB2CPartner(string integrationLogin)
        {
            var isB2CPartner = CheckIsB2CPartnerHelper.CheckIsB2CPartner(_entitySettingsManager, integrationLogin);

            return isB2CPartner;
        }

        /// <summary>
        /// Функция для проверки, есть ли ручные заявки на проверку андеррайтера.
        /// </summary>
        /// <param name="basket">Текущая корзина со сделками.</param>
        /// <returns>Возвращает true, если нет ручных заявок или они со статусами Accepted.</returns>
        public bool HasDIUnderwriterIsSystemCreatedApproval(Basket basket)
        {
            var nonSystemCreatedApprovals = basket.GetKaskoDeal().AutoDeal.DIUnderwriterApprovals?.Where(ua => ua.IsSystemCreated == false);

            if (nonSystemCreatedApprovals?.Any() != true)
            {
                return true;
            }

            return nonSystemCreatedApprovals.All(ua =>
                DiUnderwriterApprovalStatusCodes.AcceptedStatuses.Contains(ua.StatusCode)
                || (ua.ThemeCode == DIUnderwriterApprovalThemeCodes.Link2PersDiscount && ua.StatusCode == DiUnderwriterApprovalStatusCodes.Rejected));
        }

        public bool HasManualUnderwriterApprovals(Basket basket)
        {
            var autoDeal = basket.GetKaskoDeal().AutoDeal;
            var diUnderwriterApprovals = autoDeal.DIUnderwriterApprovals;
            return diUnderwriterApprovals.Any(x => x.IsSystemCreated == false);
        }

        public bool IsAutoCostInAcceptedLimits(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var auto = deal.GetAuto();
            var autoCost = auto.AutoCost;

            var isAutoCostInLimits = deal.AutoDeal.DIUnderwriterApprovals
                .Where(x => 
                    x.AutoCostApproval.HasValue
                    && DiUnderwriterApprovalStatusCodes.AcceptedStatuses.Contains(x.StatusCode)
                    && DIUnderwriterApprovalThemeCodes.Link2ThemesToCheckAutoCost.Contains(x.ThemeCode))
                .GroupBy(x => x.ThemeCode)
                .Select(g => g.OrderByDescending(z => z.CreateDate).FirstOrDefault())
                .All(x => IsValid(autoCost, x.AutoCostApproval, x.ThemeCode, deal.IsProlongation));

            return isAutoCostInLimits;

            bool IsValid(decimal? autocost, decimal? acceptedLimit, string themeCode, bool? isProlongation) => themeCode switch
            {
                (DIUnderwriterApprovalThemeCodes.VehCostOver15000000ForLink2 or
                DIUnderwriterApprovalThemeCodes.VehCostOver5000000ForLink2 or
                DIUnderwriterApprovalThemeCodes.VehCostOver7000000ForLink2) when
                    isProlongation != true && autocost <= acceptedLimit => true,

                DIUnderwriterApprovalThemeCodes.VehCostIsAboveRangeForLink2 when
                    autocost <= acceptedLimit => true,

                DIUnderwriterApprovalThemeCodes.VehCostOver10000000ForProlongationLink2 when
                    isProlongation == true && autocost <= acceptedLimit => true,

                DIUnderwriterApprovalThemeCodes.VehCostIsBelowRangeForLink2 when
                    isProlongation == true && autocost >= acceptedLimit => true,

                _ => false,
            };
        }

        public bool IsAutoCostInAcceptedLimits()
        {
            var basket = _context.GetBasket();
            return IsAutoCostInAcceptedLimits(basket);
        }

        /// <summary>
        /// Проверка признака апсейла.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <returns>bool.</returns>
        public bool CheckIsUpsale(Basket basket)
        {
            var isUpsale = basket.IsUpsale();

            _logger
                .ForContext<DiKaskoExternalFunctionsInvoker>()
                .ForContext("ProcessKey", basket.Number)
                .Information($"Проверка признака апсейла: {isUpsale}.");

            return isUpsale;
        }

        public bool HasUrmApprovals(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastUrmApproval(out var approval))
            {
                if (approval?.URMApprovalStatusCode != null)
                {
                    return true;
                }
            }

            return false;
        }

        public bool HasChecksInWork(Basket basket)
        {
            if (basket.EntityContext.TryGetValue(nameof(PreInsuranceCheckModel), out PreInsuranceCheckModel model) && model != null)
            {
                return model.Status != UrmApprovalStatusCodes.Accepted;
            }

            var deal = basket.GetKaskoDeal();
            var sasIsRejected = deal.AutoDeal.SASApproval.Any(x => x.SASApprovalStatusCode == SasApprovalStatusCodes.Rejected);

            return deal.DealStatus.Code == "InWork" && !sasIsRejected;
        }

        public Basket DeepCopyBasket(Basket inputBasket)
        {
            if (inputBasket == null)
            {
                return null;
            }

            var basket = inputBasket.DeepCopy();
            basket.Id = 0;
            basket.EntityId = 0;

            return basket;
        }
    }
}

# Code\ExternalFunctions\DiKaskoProlongationTestModeChecker.cs
﻿using System;

using iRIS.Framework.Entity.Dictionary.EntitySettings;
using iRIS.Framework.Statement.Engine;
using iRIS.InsuranceCore;
using iRIS.KaskoLink2.Code.Helpers;

using Microsoft.Extensions.Options;

namespace iRIS.KaskoLink2.Code.ExternalFunctions
{
    [Obsolete]
    public class DiKaskoProlongationTestModeChecker : IExternalFunctionsInvoker
    {
        private readonly IEntitySettingsManager _entitySettingsManager;
        private readonly IOptions<InsuranceCoreModuleConfiguration> _insuranceCoreConfiguration;
/// <summary>
        /// Проверка, что есть необходимость отправки в Siebel.
        /// </summary>
        /// <param name="authData">Авторизационная дата.</param>
        /// <param name="saveDraft"></param>
        /// <param name="isLead"></param>
        /// <param name="isProlongation"></param>
        /// <param name="getKaskoProlongationSendToSiebel"></param>
        /// <returns>True если есть необходимость отправки в Siebel.</returns>
        public bool CheckNeedSendSiebel(AuthorizationData authData, string saveDraft, bool isLead, bool isProlongation, bool getKaskoProlongationSendToSiebel)
        {
            var isLink2DiAgents = CurrentUserIsLink2DiAgents(authData);
            return (isLink2DiAgents && saveDraft == "True")
                         || (!isLink2DiAgents && isLead)
                         || (!isLink2DiAgents && isProlongation && getKaskoProlongationSendToSiebel);
        }

        /// <summary>
        /// Проверка, что пользователь является Link2DiAgent.
        /// </summary>
        /// <param name="authData">Авторизационная дата.</param>
        /// <returns>True если партнер Link2DiAgent.</returns>
        private bool CurrentUserIsLink2DiAgents(AuthorizationData authData)
        {
            var groupNames = GetUserGroups(authData);

            return groupNames.Contains(UserGroups.Link2DiAgents);
        }

        private IEnumerable<string> GetUserGroups(AuthorizationData authorizationData)
        {
            if (authorizationData.IsAnonymous)
            {
                return new[] { UserGroups.Anonymous };
            }

            if (authorizationData.IsRegistered)
            {
                return new[] { UserGroups.Registered };
            }

            if (authorizationData.IsAuthenticated)
            {
                var user = GetUser(authorizationData.CurrentUserId);
                if (user?.Groups != null)
                {
                    return user.Groups.Select(g => g.Name).ToArray();
                }
            }

            return Array.Empty<string>();
        }

        private bool DriversIsFilled(Deal deal)
        {
            if (deal == null || deal.AutoDeal == null)
            {
                return false;
            }

            var multidrive = deal.AutoDeal.IsMultidrive == true;

            if (multidrive)
            {
                return multidrive;
            }

            if (deal.AutoDeal.Drivers == null)
            {
                return false;
            }

            bool driversIsFilled = true;

            //Driver.Subject.IndividualEntity.Person.Age
            //Driver.DrivingExperiences[0].FullDrivingExperience
            foreach (var driver in deal.AutoDeal.Drivers)
            {
                driversIsFilled &= driver?.Subject?.IndividualEntity?.Person?.Age.HasValue == true;
                driversIsFilled &= driver?.DrivingExperiences?.FirstOrDefault()?.FullDrivingExperience.HasValue == true;
            }

            return driversIsFilled;
        }

        private bool AutoIsFilled(Deal deal)
        {
            var auto = deal.GetAuto();
            return
                !string.IsNullOrEmpty(auto.AutoBrandCode)
                && !string.IsNullOrEmpty(auto.AutoModelCode)
                && !string.IsNullOrEmpty(auto.AutoYearOfIssueCode)
                && !string.IsNullOrEmpty(auto.AutoEnginePowerCode);
        }

        public bool CheckDealWithoutInspection(Basket basket)
        {
            var isWithoutInspection = _checkManager.DealWithoutInspection(basket);
            return isWithoutInspection;
        }

        // https://jira.renins.com/browse/IRIS-159103
        public string GetUpsalesInfo(Basket basket)
        {
            return string.Empty;
        }

        public bool CheckIsAllowedToUseBSIForLink2(Basket basket)
        {
            var isCheckHistoryInBsi = basket.GetKaskoDeal()?.AutoDeal.IsCheckHistoryInBsi ?? false;

            return CheckIsAllowedToUseBsi(basket) && isCheckHistoryInBsi;
        }

        public bool CheckIsAllowedToUseBSIForCallCenter(Basket basket)
        {
            return CheckIsAllowedToUseBsi(basket);
        }

        private bool CheckIsAllowedToUseBsi(Basket basket)
        {
            return _bsiRequestAllowanceChecker.CheckIsAllowedToUseBsi(basket);
        }

        public decimal GetSellerCoefficientMin(Basket basket)
        {
            var osagoDeal = basket.GetOsagoDeal();
            var sellerCoefMin = _sellerCoefficientManager.GetSellerCoefMin(osagoDeal);

            return sellerCoefMin;
        }

        /// <summary>
        /// AB-Test переход на конструктор клиента.
        /// </summary>
        /// <returns><see href="true"/> - переход на конструктор клиента.</returns>
        public bool IsShowClientConstructor() => _abTestHelper.IsShowClientConstructor();

        /// <summary>
        /// Проверяет что все сделки в корзине пролонгационные, или нет.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="examineProlongationValue">Значение признака пролонгации сделки, да или нет.</param>
        /// <returns>Да или нет.</returns>
        public bool CheckForProlongation(Basket basket, bool examineProlongationValue)
        {
            return basket.Deals
                .Where(x => x.IsInBasket == true)
                .All(x => x.IsProlongation == examineProlongationValue);
        }

        /// <summary>
        /// Доступность пролонгации.
        /// </summary>
        /// <param name="authorizationData"></param>
        /// <param name="basket">Корзина.</param>
        /// <returns> Значение настройки KaskoProlongationSendToSiebel</returns>
        public bool GetAvailableProlongation(AuthorizationData authorizationData, Basket basket)
        {
            var loginName = GetIntegrationLoginName(authorizationData, basket);
            var intermediary = GetKaskoIntermediary(basket);
            var deal = basket.GetKaskoDeal();

            if (deal.IsProlongation == true && !IsAllowUpdateCostCenterForProlongation(intermediary))
            {
                var parentDealCostCenterNameFromOss = deal.ParentDeal.CostcenterNameFromOss;
                var channels = _entitySettingsManager.GetSettingForDefaultOrPartnerConfiguration<List<string>>(
                    EntitySettingCodes.AvailableProlongationSalesChannel,
                    EntitySettingCodes.AvailableProlongationSalesChannelDefault,
                    EntitySettingCodes.AvailableProlongationSalesChannelPartner,
                    new IntegrationLogin()
                    {
                        IntegrationLoginName = loginName
                    },
                    new List<string>(),
                    _logger);

                if (channels.Contains(FullAvailableSalesChannel) ||
                    (channels.Any(c => parentDealCostCenterNameFromOss.Contains(c))
                    && channels.Contains(deal.CostcenterCode)))
                {
                    return true;
                }

                _validationResultsAggregator.AppendValidationErrors(new ValidationError()
                {
                    ErrorMessage = "Код: 300027.Расчет пролонгации недоступен для данного КП",
                    CriticalLevel = ValidationCriticalLevel.CantContProc
                });

                return false;
            }

            return true;
        }

        /// <summary>
        /// Получение персоны в зависимости от ответственного за ПСО.
        /// </summary>
        public Person GetInspectionPerson(Basket basket, string psoResponsibleCode)
        {
            if (psoResponsibleCode == InspectionResponsibleCodes.CurrentUser)
            {
                var intermediary = basket
                    .GetKaskoDeal()
                    .GetIntermediary();

                if (intermediary == null)
                {
                    throw new ArgumentNullException("Не указан посредник с ролью Агент или Продавец");
                }

                return intermediary.Subject
                    .IndividualEntity
                    .Person;
            }

            return basket.GetKaskoDeal().Insurant.Subject.IndividualEntity.Person;
        }

        #region Obsolete функции для поддержки старых версий процессов
        [Obsolete("Удалить после апреля 2024го года")]
        public bool IsPromoCodeApplied(Basket basket)
        {
            return _discountApplier.IsPromoCodeAppliedAtKaskoDeal(basket);
        }

        [Obsolete("Удалить после апреля 2024го года")]
        public bool CheckIsRusfinancebank(Basket basket)
        {
            return basket.HasIntermediary(IntermediaryCodes.Rusfinancebank);
        }

        /// <summary>
        /// Получить UserId страхователя.
        /// </summary>
        [Obsolete("Удалить после апреля 2024го года")]
        public int GetInsurantUserId(Basket basket)
        {
            var person = basket.GetKaskoDeal().GetInsurantPerson();

            var user = _queryUser.GetUserByEmail(person.Email1);

            return user.Id;
        }

        /// <summary>
        /// Оплата просрочена.
        /// </summary>
        /// <param name="basket">корзина.</param>
        /// <returns> Если оплата просрочена - true.</returns>
        [Obsolete("Удалить после апреля 2024го года")]
        public bool IsOverduePayment(Basket basket)
        {
            var deals = basket
              .Deals
              .Where(d => d.IsInBasket == true && !d.DealStatusCode.InEnum(EntityStatusType.Signed, EntityStatusType.PolicyIssued));

            var dateRepayment = deals
            .Select(d =>
            {
                _paymentInvoiceInitializer.InitializeInvoiceByDeal(d, out var dateRepay);
                return dateRepay;
            })
            .Min();

            return dateRepayment != null && dateRepayment < DT.Now;
        }
        #endregion

        // Логика LKA-151
        private bool IsAllowUpdateCostCenterForProlongation(Intermediary intermediary)
        {
            var settingValue = _entitySettingsManager.GetSettingForDefaultOrPartnerConfiguration(
                EntitySettingCodes.AllowUpdateCostCenterForProlongation,
                EntitySettingCodes.AllowUpdateCostCenterForProlongationDefault,
                EntitySettingCodes.AllowUpdateCostCenterForProlongationPartner,
                intermediary,
                false,
                _logger);
            return settingValue;
        }

        public Intermediary GetIsMainIntermediary(Basket basket)
        {
            return basket.GetKaskoDeal().DealIntermediaries.Single(di => di.IsMain == true).Intermediary;
        }

        public string GetSas(Basket basket)
        {
            return basket.GetKaskoDeal().AutoDeal.AutoCheckResults.Any(acr => acr.Name == "SasDataKaskoCheck")
                ? "[SAS]"
                : string.Empty;
        }

        public string GetOtaIsRepeated(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval) && approval.IsRepeated == true)
            {
                return "Повторно. ";
            }
            else
            {
                return string.Empty;
            }
        }

        public string GetUrmApprovalInfoMarkup(Basket basket)
        {
            var urmApproval = basket.GetKaskoDeal().AutoDeal.GetLastUrmApproval();
            var discount = GetUrmDiscount(basket);
            var markup = new StringBuilder("Добрый день!<br/>");
            markup.Append($"<br/>Статус согласования: {urmApproval.URMApprovalStatus.Name}<br/>");
            if (discount != null && discount.DiscountValue.HasValue && discount.DiscountValue.Value != 1m)
            {
                markup.Append($"<br/>Коэффициент УРМ: {discount.DiscountValue.Value.ToString(CultureInfo.InvariantCulture)}<br/>");
            }

            markup.Append($"<br/>Комментарий: {urmApproval.URMApprovalComment}<br/>");

            return markup.ToString();
        }

        public string GetOtaApprovalInfoMarkup(Basket basket)
        {
            var markup = new StringBuilder();
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                markup.Append($"Статус проверки: {approval.OtaApprovalStatus.Name}");
                foreach (var checkError in approval.OtaApprovalCheckErrors)
                {
                    markup.Append($"<br/>Наименование ошибки: {checkError.OtaApprovalMistake.Name}");
                    if (!string.IsNullOrEmpty(checkError.ErrorComment))
                    {
                        markup.Append($"<br/>Комментарий к ошибке: {checkError.ErrorComment}");
                    }
                }

                if (approval.OtaApprovalStatusCode == OtaApprovalStatusCodes.NonCriticalMistakes)
                {
                    markup.Append($"<br/>Возможно дальнейшее оформление полиса");
                }
            }

            return markup.ToString();
        }

        public bool IsNecessaryToRecalculateDueToUrmCoefficient(Basket basket)
        {
            var discount = GetUrmDiscount(basket);

            return discount != null && discount.DiscountSwitchOnOff == true && discount.DiscountValue != 1m;
        }

        /// <summary>
        /// Проверка необходимости захода в подпроцесс УРМ (подпроцесс возобновления).
        /// </summary>
        /// <param name="basket"></param>
        /// <returns>true, если УРМ в статусе, доступном для редактирования, либо завершена и нет проверок ОТА/ПСО (для корректной работы основного процесса).</returns>
        public bool IsUrmStepAccessible(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            if (deal.DealStatusCode != EntityStatusType.InWork.ToString())
            {
                return false;
            }

            if (!basket.GetKaskoDeal().AutoDeal.TryGetLastUrmApproval(out var approval))
            {
                return false;
            }

            // Для случая, когда назначена только проверка УРМ после выставления финального статуса
            // необходимо еще раз зайти в подпроцесс и пройти по нему до смены статусов корзины и сделок.
            if (UrmStatusHelper.UrmCompletedStatuses.Contains(approval.URMApprovalStatusCode) && !IsOtaStepAdded(basket))
            {
                var inspection = _mediator.ResolveQuery<IQueryKaskoInspection>().GetInspectionByDealEntityId(deal.EntityId);

                return inspection == null;
            }

            return UrmStatusHelper.UrmAddedStatuses.Contains(approval.URMApprovalStatusCode);
        }

        public bool IsUrmStatusInWork(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastUrmApproval(out var approval))
            {
                return approval.URMApprovalStatusCode.Equals(UrmApprovalStatusCodes.InWork, StringComparison.OrdinalIgnoreCase);
            }

            return false;
        }

        public bool CheckUrmApprovalStatusEqualsTo(Basket basket, string status)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastUrmApproval(out var approval))
            {
                return approval.URMApprovalStatusCode == status;
            }

            return false;
        }

        public bool CheckOtaApprovalStatusEqualsTo(Basket basket, string status)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                return approval.OtaApprovalStatusCode == status;
            }

            return false;
        }

        /// <summary>
        /// Проверка необходимости прохождения подпроцесса ОТА/ПСО (подпроцесс ОТА/ПСО).
        /// </summary>
        /// <param name="basket"></param>
        /// <returns></returns>
        public bool IsOtaPsoStepNecessary(Basket basket)
        {
            var deal = basket.GetKaskoDeal();

            var inspection = _mediator.ResolveQuery<IQueryKaskoInspection>().GetInspectionByDealEntityId(deal.EntityId);
Какие классы связаны с расчетом апсейлов?