Отвечай на вопросы, используя следующий контекст:
/// <summary>
        /// Пытается выполнить функтор и при его краше логгирует эксепшен
        /// </summary>
        /// <typeparam name="TResult">Тип возвращаемого значения функтора</typeparam>
        /// <param name="businessLogic">Функтор</param>
        /// <param name="logMessage">Понятное сообщение для эксепшена</param>
        /// <returns>Результат функтора или эксепшен</returns>
        private TResult TryDoSomethingAndWriteLogIfItsCrash<TResult>(Func<TResult> businessLogic, string logMessage)
        {
            try
            {
                return businessLogic.Invoke();
            }
            catch (Exception ex)
            {
                var log = ServiceProvider.GetService<ILogger>();
                log.Error(ex, logMessage);
                throw;
            }
        }

        /// <summary>
        /// Заполняет коэффициент пролонгации для сделки.
        /// Если значение уже задано, оно остается без изменений.
        /// </summary>
        /// <param name="basket">корзина.</param>
        /// <param name="renewalStructId">страктИД откуда брать вычисленное значение.</param>
        public void SetRenewalCoefficient(Basket basket, int renewalStructId)
        {
            if (basket == null)
            {
                throw new ArgumentNullException(nameof(basket));
            }

            var deals = basket.Deals.Where(d => d.AutoDeal != null && !d.AutoDeal.RenewalCoefficient.HasValue).ToList();
            if (deals.Any())
            {
                foreach (var deal in deals)
                {
                    if (deal.AutoDeal == null)
                    {
                        continue;
                    }

                    var proposal = deal.GetSelectedProposal() ?? deal.Proposals.FirstOrDefault();
                    if (proposal == null
                        || proposal.ComponentCalculationResults == null
                        || !proposal.ComponentCalculationResults.Any())
                    {
                        continue;
                    }

                    var calcResult = proposal.ComponentCalculationResults.FirstOrDefault(ccr => ccr.CalculationComponentSetting != null && ccr.CalculationComponentSetting.StructId == renewalStructId);
                    deal.AutoDeal.RenewalCoefficient = calcResult?.Value;
                }
            }
        }

        /// <summary>
        /// Заполняет значение минимальной скидки для пролонгации.
        /// Если значение уже задано, оно остается без изменений.
        /// </summary>
        /// <param name="basket">корзина.</param>
        /// <param name="settingStructId">страктИД откуда брать вычисленное значение.</param>
        public void SetProlongationDiscountLimit(Basket basket, int settingStructId)
        {
            if (basket == null)
            {
                throw new ArgumentNullException(nameof(basket));
            }

            var deals = basket.Deals.Where(d => d.AutoDeal != null && !d.AutoDeal.ProlongationDiscountLimit.HasValue).ToList();
            if (deals.Any())
            {
                foreach (var deal in deals)
                {
                    if (deal.AutoDeal == null)
                    {
                        continue;
                    }

                    var proposal = deal.GetSelectedProposal() ?? deal.Proposals.FirstOrDefault();
                    if (proposal == null
                        || proposal.ComponentCalculationResults == null
                        || !proposal.ComponentCalculationResults.Any())
                    {
                        continue;
                    }

                    var calcResult = proposal.ComponentCalculationResults.FirstOrDefault(ccr => ccr.CalculationComponentSetting != null && ccr.CalculationComponentSetting.StructId == settingStructId);
                    deal.AutoDeal.ProlongationDiscountLimit = calcResult?.Value;
                }
            }
        }

        /// <summary>
        /// Очистка коэффициентов перед повторным вычислением стоимости сделки.
        /// Для их обновления в расчете.
        /// </summary>
        /// <param name="basket">корзина.</param>
        public void ClearCoefficientBeforeRecalculation(Basket basket)
        {
            if (basket == null)
            {
                throw new ArgumentNullException(nameof(basket));
            }

            var deals = basket.Deals.Where(d => d.AutoDeal != null && d.AutoDeal.RenewalCoefficient.HasValue).ToList();
            if (deals.Any())
            {
                foreach (var deal in deals)
                {
                    if (deal.AutoDeal == null)
                    {
                        continue;
                    }

                    deal.AutoDeal.RenewalCoefficient = null;
                }
            }
        }

        /// <summary>
        /// Очистка значения минимальной скидки для пролонгации перед повторным вычислением стоимости сделки.
        /// Для их обновления в расчете.
        /// </summary>
        /// <param name="basket">корзина.</param>
        public void ClearProlongationDiscountLimit(Basket basket)
        {
            if (basket == null)
            {
                throw new ArgumentNullException(nameof(basket));
            }

            var deals = basket.Deals.Where(d => d.AutoDeal != null && d.AutoDeal.ProlongationDiscountLimit.HasValue).ToList();
            if (deals.Any())
            {
                foreach (var deal in deals)
                {
                    if (deal.AutoDeal == null)
                    {
                        continue;
                    }

                    deal.AutoDeal.ProlongationDiscountLimit = null;
                }
            }
        }

        /// <summary>
        /// Удаление обычного оффера конструктора, если есть специальный конструктор для лиников.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void RemoveDuplicateConstructorOffer(Basket basket)
        {
            Link2FunctionsInvoker invoker = ServiceProvider.GetService<Link2FunctionsInvoker>();

            bool isLink2 = invoker.BasketCreatorIsLink2Partner(basket);
            var constructorOffer = basket.Offers.FirstOrDefault(x => x.Id == DiKaskoOfferIds.Constructor);
            var link2ConstructorOffer = basket.Offers.FirstOrDefault(x => x.Id == DiKaskoOfferIds.Link2Constructor);

            if (constructorOffer != null && link2ConstructorOffer != null)
            {
                basket.Offers.Remove(isLink2 ? constructorOffer : link2ConstructorOffer);
            }
        }

        /// <summary>
        /// Получить реккурентный токен.
        /// </summary>
        /// <param name="basket">Корзина</param>
        /// <returns>Реккурентный токен</returns>
        public string GetRecurringToken(Basket basket)
        {
            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);

            try
            {
                var queryBasket = (IQueryBasket)ServiceProvider.GetRequiredService(typeof(IQueryBasket));
                var client = (IUniversalOnlinePaymentClient)ServiceProvider.GetRequiredService(typeof(IUniversalOnlinePaymentClient));

                var prevPolicyNumber = basket.Deals
                    .LastOrDefault(d => d.AutoDeal?.IsSubscription == true && !string.IsNullOrEmpty(d.AutoDeal?.PreviousPolicyNumber))
                    ?.AutoDeal?.PreviousPolicyNumber;

                if (!string.IsNullOrEmpty(prevPolicyNumber))
                {
                    var includes = new List<string>()
                    {
                        nameof(Basket.Payments)
                    };

                    var parentBasket = queryBasket.GetBasketsByPolicySeriesAndNumber(string.Empty, prevPolicyNumber, includes)
                        ?.Where(x => x.Payments.Count > 0)
                        ?.OrderByDescending(x => x.Id)
                        ?.FirstOrDefault();

                    if (parentBasket != null)
                    {
                        var hash = parentBasket.Payments.FirstOrDefault()?.Hash;

                        if (!string.IsNullOrEmpty(hash))
                        {
                            return client.GetPaymentData(hash)?.RecurringToken;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                log.Error(ex, $"Ошибка при выполения метода GetRecurringToken.");
            }

            return null;
        }

        [Obsolete("Устарел. Использовать SetIsClosedDIUnderwriterApprovals(Basket , userId). Удалить после 01.10.2024")]
        public void SetIsClosedDIUnderwriterApprovals(Basket basket)
        {
            SetIsClosedDIUnderwriterApprovals(basket, null);
        }

        /// <summary>
        /// Закрыть заявки андеррайтера.
        /// </summary>
        /// <param name="basket">Корзина</param>
        public void SetIsClosedDIUnderwriterApprovals(Basket basket, int? userId)
        {
            foreach (var approval in basket.GetKaskoDeal().AutoDeal.DIUnderwriterApprovals)
            {
                if (string.IsNullOrEmpty(approval.StatusCode)
                    || approval.IsClosed == true)
                {
                    continue;
                }

                approval.IsClosed = true;
                approval.DIUnderwriterClosedUserId = userId;
                approval.RequestDateEnd ??= DT.Now;
            }
        }

        private static readonly List<string> _notRequiredFileBlocks = new List<string>
        {
            PersonalDiscountFile,
            OtherInsuranceCompanyPolicy,
            DiagnosticCardDocuments
        };

        public void SetRequiredForFileBlocks(Basket basket, bool isLink2)
        {
            if (isLink2)
            {
                var feFinder = this.ServiceProvider.GetRequiredService<IFileEntityFinder>();
                var fileBlocks = feFinder.FindFileBlocks(new[] { basket.GetKaskoDeal() });

                foreach (var fileBlock in fileBlocks)
                {
                    var block = fileBlock.FileEntity.ObjectInstance as FileBlock;
                    foreach (var blockFileGroup in block.FileGroups)
                    {
                        if (blockFileGroup.MinFilesCount == 0 && !_notRequiredFileBlocks.Contains(blockFileGroup.Name))
                        {
                            blockFileGroup.MinFilesCount = 1;
                        }
                    }
                }
            }
        }

        public bool CheckRequiredForFileBlocks(Basket basket)
        {
            var feFinder = this.ServiceProvider.GetRequiredService<IFileEntityFinder>();
            var fileBlocks = feFinder.FindFileBlocks(new[] { basket.GetKaskoDeal() });

            return fileBlocks.Any(b => (b.FileEntity.ObjectInstance as FileBlock).FileGroups.Any(g => g.MinFilesCount > 0 && !g.FileInfos.Any()));
        }

        private bool IsLink2Upsale(Basket basket)
        {
            var mediator = (ICqrsMediator)ServiceProvider.GetService(typeof(ICqrsMediator));
            var currentDate = basket.DateActual ?? DT.Now;
            var segmentWrappers = mediator.ResolveQuery<IQuerySegment>().GetSegments(basket.Segments.Select(s => s.Id).ToArray(), currentDate, basket.ExternalTagList?.UseDrafts);
            var isUpsale = segmentWrappers.Count() > 0 && segmentWrappers.All(s => s.Segment.SegmentUsageLogicCode == Link2UpsaleConstants.UpsaleSegmentUsageLogicCode && s.Segment.Offers != null && s.Segment.Offers.Count > 0);
            return isUpsale;
        }

        public void SetEmailSentFlag(Basket basket)
        {
            var cache = ServiceProvider.GetRequiredService<ICache>();
            var basketNumber = basket.Number;
            var key = $"DiKasko_EpolicyEmailSent_{basketNumber}";

            if (cache.Get<bool?>(key) == null)
            {
                cache.Set(key, true, 150000);
            }
        }

        public bool GetEmailSentFlag(Basket basket)
        {
            var cache = ServiceProvider.GetRequiredService<ICache>();
            var basketNumber = basket.Number;
            var key = $"DiKasko_EpolicyEmailSent_{basketNumber}";

            bool emailSendFlag = false;
            if (cache.Get<bool?>(key) == true)
            {
                emailSendFlag = true;
            }

            return emailSendFlag;
        }

        /// <summary>
        /// Установка даты старта полиса для сделки день в день b2p
        /// </summary>
        /// <param name="basket"></param>
        /// <param name="integrationLoginName"></param>
        public void SetDealDateStartDueToNow(Basket basket, string integrationLoginName)
        {
            var deal = basket.GetKaskoDeal();

            if (!CheckIsB2CPartner(integrationLoginName, basket.EntityId) && deal.DateStart.Value.Date == DT.Today)
            {
                var cache = ServiceProvider.GetRequiredService<ICache>();
                var basketNumber = basket.Number;
                var key = $"DiKasko_DateStartDueToNow_{basketNumber}";

                if (cache.Get<DateTime>(key) == DateTime.MinValue)
                {
                    cache.Set(key, UpdateDealDateStartDueToNow(deal), 150000);
                }
            }
        }

        private DateTime UpdateDealDateStartDueToNow(Deal deal)
        {
            if (deal.DateStart.Value.Date == DT.Today)
            {
                deal.DateStart = DT.Now.AddHours(1);
            }

            return deal.DateStart.Value;
        }

        /// <summary>
        /// Удаление промокода из сделки, если он удален из базы
        /// </summary>
        public bool ClearPromocodeIfItRemoved(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var promocodeManager = ServiceProvider.GetRequiredService<IPromocodeManager>();
            var isNeedClearPromocode = false;
            var removedPromocodeId = string.Empty;

            foreach (var proposal in deal.Proposals)
            {
                var selectedDiscountCombination = proposal?.GetSelectedDiscountCombination();
                if (selectedDiscountCombination == null)
                {
                    continue;
                }

                var entityDiscounts = selectedDiscountCombination.Discounts;
                var promocodeDiscount = entityDiscounts.SingleOrDefault(d => d.DiscountTypeCode == DiscountCombinationConst.DiscountType_Promocode);

                if (promocodeDiscount == null || !promocodeDiscount.PromoCode_Id.HasValue)
                {
                    continue;
                }

                //уже используется какой-либо промокод
                var promoCode = promocodeManager.GetActualPromocode(promocodeDiscount.PromoCode_Id.Value, deal.Product_Id.Value);

                if (promoCode == null)
                {
                    isNeedClearPromocode = true;
                    removedPromocodeId = promocodeDiscount.PromoCode_Id.Value.ToString();
                    break;
                }
            }

            if (isNeedClearPromocode)
            {
                ResetPromocode(basket);
                var logger = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);
                logger.Warning($"Удален промокод из корзины, так как был удален из базы PromocodeId: {removedPromocodeId}");
            }

            return isNeedClearPromocode;
        }
/// <summary>
        /// Установка статуса УРМ.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="sendMailOPKKK">Признак отправки письма в ОПККК.</param>
        public void SetURMApprovalStatus(Basket basket, bool sendMailOPKKK)
        {
            UrmApprovalStatusHelper.SetURMApprovalStatus(basket, sendMailOPKKK);
        }

        /// <summary>
        /// Установка признака повторного запроса в УРМ.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetURMApprovalIsRepeated(Basket basket)
        {
            var kaskoDeal = basket.GetKaskoDeal();

            if (kaskoDeal.IsInBasket != true)
            {
                return;
            }

            var approval = kaskoDeal.AutoDeal.GetLastUrmApproval();

            approval.IsRepeated = true;
        }

        /// <summary>
        /// Смена статуса проверок андеррайтера на статус "В работе".
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetDIUnderwriterApprovalStatusInWork(Basket basket)
        {
            var kaskoDeal = basket.GetKaskoDeal();
            foreach (var approval in kaskoDeal.AutoDeal.DIUnderwriterApprovals)
            {
                if (approval.StatusCode == DiUnderwriterApprovalStatusCodes.Required || approval.StatusCode == DiUnderwriterApprovalStatusCodes.RequiredExtraInfo || string.IsNullOrEmpty(approval.StatusCode))
                {
                    approval.StatusCode = DiUnderwriterApprovalStatusCodes.InWork;
                    approval.RequestCreateDate ??= DT.Now;
                }
            }
        }

        /// <summary>
        /// Смена флага покупки полиса под клиентом.
        /// </summary>
        public void SetConfirmedByClient(Basket basket, bool value, bool forEPolicy)
        {
            var kasko = basket.GetDealByProductDictCode(ProductDictCodes.Kasko);
            var osago = basket.GetDealByProductDictCode(ProductDictCodes.Osago);

            if ((kasko?.IsEPolicy == true || osago?.IsEPolicy == true) && forEPolicy != true)
            {
                return;
            }

            basket.ConfirmedByClient = value;
        }

        /// <summary>
        /// Проверить наличие одинаковых водителей (В процессе КАСКО проверяется только для ОСАГО, так как в КАСКО нет точных полей ФИО + Д.Р.).
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <returns>Флаг наличия одинаковых водителей.</returns>
        public bool CheckDublicateDriversForKaskoProcess(Basket basket)
        {
            return false;

            var kaskoDeal = basket.GetKaskoDeal();

            if (kaskoDeal == null || kaskoDeal.AutoDeal == null || kaskoDeal.AutoDeal.IsMultidrive.GetValueOrDefault(false))
            {
                return false;
            }

            var drivers = kaskoDeal.AutoDeal.Drivers;

            var driversGroups = drivers.GroupBy(i => new
            {
                i.DriverLicenseSeries,
                i.DriverLicenseNumber,
                i.Subject.IndividualEntity.Person.Name,
                i.Subject.IndividualEntity.Person.LastName,
                i.Subject.IndividualEntity.Person.MiddleName,
                i.Subject.IndividualEntity.Person.DriveExperience,
                i.Subject.IndividualEntity.Person.DateOfBirth
            });

            return drivers.Count() != driversGroups.Count();
        }

        /// <summary>
        /// Проверить доступность оформления полиса без осмотра через мобильное приложение.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <returns>true, если нет ПСО и нет ни одной проверки.</returns>
        public bool CheckDealWithoutInspection(Basket basket)
        {
            var checkManager = ServiceProvider.GetService<ICheckInspectionManager>();
            var isWithoutInspection = checkManager.DealWithoutInspection(basket);
            var kaskoDeal = basket.GetKaskoDeal();

            bool underwriterApprovalsAdded = kaskoDeal.AutoDeal.DIUnderwriterApprovals
                 .Any(o => o.StatusCode == DiUnderwriterApprovalStatusCodes.Required);

            bool otaApprovalsAdded = kaskoDeal.AutoDeal.OtaApproval
                .Any(o => o.OtaApprovalStatusCode == OtaApprovalStatusCodes.Required);

            bool urmApprovalsAdded = kaskoDeal.AutoDeal.URMApproval
                .Any(o => o.URMApprovalStatusCode == UrmApprovalStatusCodes.Required);

            return isWithoutInspection && !underwriterApprovalsAdded && !otaApprovalsAdded && !urmApprovalsAdded;
        }

        /// <summary>
        /// Проверка необходима ли проверка полиса Каско в SAS для корзины.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="authorizationData">Данные авторизации.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsSasRequired(Basket basket, AuthorizationData authorizationData)
        {
            if (!authorizationData.IsAuthenticated)
            {
                return false;
            }

            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);
            var settingsManager = (IEntitySettingsManager)ServiceProvider.GetService(typeof(IEntitySettingsManager));

            //  запрос в САС требуется для всех случаев, кроме пролонгации, так же может быть выключен именованной настройкой
            if (IsNotProlongation(basket) &&
                settingsManager.TryGetSettingValue<bool>(
                    KaskoSasCheckProcessConstants.KaskoSasCheckProcessSettings,
                    out var checkKaskoSasRequired,
                    x => x.Where(c => c.Code == KaskoSasCheckProcessConstants.KaskoSasCheckProcessDefault)))
            {
                log.Information($"CheckIsSasRequired. EntitySetting value is {checkKaskoSasRequired}");
                return checkKaskoSasRequired;
            }

            return false;
        }

        /// <summary>
        /// Проверка возможности вызова BSI.
        /// </summary>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsAllowedToUseBSI(string integrationLogin)
        {
            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.IsAllowedToUseBSI,
                KaskoEntitySettingCodes.IsAllowedToUseBSIDefault,
                KaskoEntitySettingCodes.IsAllowedToUseBSIByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false);
        }

        /// <summary>
        /// Проверка возможности обращения в Comissions.
        /// </summary>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsAllowedToGetComissions(string integrationLogin)
        {
            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();
            var log = ServiceProvider.GetRequiredService<ILogger>();

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.IsAllowedToGetComissionsOnCalculate,
                KaskoEntitySettingCodes.IsAllowedToGetComissionsOnCalculateDefault,
                KaskoEntitySettingCodes.IsAllowedToGetComissionsOnCalculateByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false,
                log);
        }

        /// <summary>
        /// Проверка признака партнера B2C.
        /// </summary>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsB2CPartner(string integrationLogin, int entityId)
        {
            var entitySettingsManager = ServiceProvider.GetService<IEntitySettingsManager>();

            if (!string.IsNullOrEmpty(integrationLogin))
            {
                return entitySettingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.IsB2CPartner,
                KaskoEntitySettingCodes.IsB2CPartnerDefault,
                KaskoEntitySettingCodes.IsB2CPartnerByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false);
            }

            var permissionManager = ServiceProvider.GetService<IPermissionManager>();
            var queryIntegrationLogin = ServiceProvider.GetService<IQueryIntegrationLogin>();

            var userId = permissionManager.GetUserIdByRelationForObject(PermissionConstants.Relation.Creator, nameof(Basket), entityId);
            var creator = queryIntegrationLogin.GetIntegrationLoginByUserId(userId.Value);

            string integrationLoginName = creator.IntegrationLoginName;

            return CheckIsB2CPartnerHelper.CheckIsB2CPartner(entitySettingsManager, integrationLoginName);
        }

        public bool CheckIsSasRequestExecuted(Basket basket)
        {
            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);
            var autoDeal = basket.GetKaskoDeal().AutoDeal;
            log.Information($"CheckIsSasRequestExecuted. Current SasApprovalStatus {autoDeal.SASApproval[0].SASApprovalStatusCode}");
            return !string.IsNullOrWhiteSpace(autoDeal.SASApproval[0].SASApprovalStatusCode);
        }

        /// <summary>
        /// Устанавливаем по умолчанию коэффицеинт САС. IRIS-103112.
        /// </summary>
        /// <param name="basket">basket.</param>
        public void SetDefaultSas(Basket basket)
        {
            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);
            var deal = basket.GetKaskoDeal();

            if (deal.AutoDeal.SasCoefficient == null || deal.AutoDeal.SasCoefficient == 0)
            {
                var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();
                var sasCoefficientHelper = ServiceProvider.GetRequiredService<SasCoefficientHelper>();
                if (settingsManager.TryGetSettingValue<decimal>("KaskoDefaultSasCoefficientValueSetting", out var settingsValue))
                {
                    sasCoefficientHelper.SetSasCoefficientToDeal(deal, basket.ExternalTagList.UseDrafts, settingsValue, 2);
                }

                log.Information($"Установка deal.AutoDeal.SasCoefficient. Значение: {settingsValue}");
            }
        }

        /// <summary>
        /// Проверка необходима ли проверка полиса Каско в SAS для корзины из Link2.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="authorizationData">Данные авторизации.</param>
        /// <param name="integrationLoginName">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsSasRequiredForLink2(Basket basket, AuthorizationData authorizationData, string integrationLoginName)
        {
            if (!authorizationData.IsAuthenticated)
            {
                return false;
            }

            var link2Invoker = ServiceProvider.GetRequiredService<Link2FunctionsInvoker>();
            var settingsManager = (IEntitySettingsManager)ServiceProvider.GetRequiredService(typeof(IEntitySettingsManager));
            var basketCreatorIsLink2Partner = link2Invoker.BasketCreatorIsLink2PartnerOrDiAgent(basket);
            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);

            if (!basketCreatorIsLink2Partner)
            {
                return false;
            }

            if (basket.IsLead == true)
            {
                settingsManager.TryGetSettingValue<string[]>(KaskoEntitySettingCodes.GetSasCoefficientOnCalculateForLead, out var partnersWithSasRequestForLead);

                if (!partnersWithSasRequestForLead.Contains(integrationLoginName))
                {
                    return false;
                }
            }

            var deal = basket.GetKaskoDeal();

            var intermediary = deal
                ?.DealIntermediaries
                ?.FirstOrDefault(x => x.Intermediary?.InteractionTypeCode == InteractionTypeEnum.Agent.ToString())
                ?.Intermediary ?? deal.GetMainIntermediary();

            var autoDeal = deal?.AutoDeal;

            if (intermediary == null || (autoDeal?.IsSubscription == true && !string.IsNullOrEmpty(autoDeal?.PreviousPolicyNumber)) || IsLink2Upsale(basket) || !IsNotProlongation(basket))
            {
                return false;
            }

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoSasCheckProcessConstants.KaskoSasCheckProcessSettings,
                KaskoSasCheckProcessConstants.KaskoSasCheckProcessDefault,
                KaskoSasCheckProcessConstants.KaskoSasCheckProcessByIntermediary,
                intermediary,
                true,
                log);
        }

        /// <summary>
        /// Проверка необходима ли проверка полиса Каско в SAS для корзины из Link2.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="authorizationData">Данные авторизации.</param>
        /// <param name="defaultSasRequired">Проверка SAS по умолчанию.</param>
        /// <param name="integrationLoginName">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        [Obsolete("Устарел, использовать CheckIsSasRequiredForLink2(Basket, AuthorizationData, string)")]
        public bool CheckIsSasRequiredForLink2(Basket basket, AuthorizationData authorizationData, bool defaultSasRequired, string integrationLoginName)
        {
            if (!authorizationData.IsAuthenticated)
            {
                return false;
            }

            var link2Invoker = ServiceProvider.GetRequiredService<Link2FunctionsInvoker>();
            var settingsManager = (IEntitySettingsManager)ServiceProvider.GetRequiredService(typeof(IEntitySettingsManager));
            var basketCreatorIsLink2Partner = link2Invoker.BasketCreatorIsLink2PartnerOrDiAgent(basket);

            if (!basketCreatorIsLink2Partner)
            {
                return false;
            }

            if (basket.IsLead == true)
            {
                settingsManager.TryGetSettingValue<string[]>(KaskoEntitySettingCodes.GetSasCoefficientOnCalculateForLead, out var partnersWithSasRequestForLead);

                if (!partnersWithSasRequestForLead.Contains(integrationLoginName))
                {
                    return false;
                }
            }

            var deal = basket.GetKaskoDeal();

            var intermediary = deal
                ?.DealIntermediaries
                ?.FirstOrDefault(x => x.Intermediary?.InteractionTypeCode == InteractionTypeEnum.Agent.ToString())
                ?.Intermediary;

            var autoDeal = deal?.AutoDeal;

            if (intermediary == null || (autoDeal?.IsSubscription == true && !string.IsNullOrEmpty(autoDeal?.PreviousPolicyNumber)) || IsLink2Upsale(basket))
            {
                return false;
            }

            // проверка необходимсоти отправки в сас по посреднику
            if (settingsManager.TryGetSettingValue<bool>(
                KaskoSasCheckProcessConstants.KaskoSasCheckProcessSettings,
                intermediary,
                out var checkKaskoSasRequired,
                x => x.Where(c => c.Code == KaskoSasCheckProcessConstants.KaskoSasCheckProcessByIntermediary)))
            {
                return checkKaskoSasRequired;
            }

            return defaultSasRequired;
        }
/// <summary>
        /// Проверка возможности оформления отложенного ПСО.
        /// </summary>
        /// <param name="integrationLoginName">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsPostponedPso(string integrationLoginName)
        {
            if (!string.IsNullOrEmpty(integrationLoginName))
            {
                var postponedPsoHelper = (IPostponedPsoHelper)ServiceProvider.GetRequiredService(typeof(IPostponedPsoHelper));
                var isPostponedPso = postponedPsoHelper.IsPostponedPso(integrationLoginName);

                return isPostponedPso;
            }

            return false;
        }

        /// <summary>
        /// Получение отложенного осмотра для сделки Каско.
        /// </summary>
        /// <returns>Осмотры для сделки Каско.</returns>
        public Inspection GetPostponedInspectionForKaskoDeal(Basket basket)
        {
            var mediator = ServiceProvider.GetService<ICqrsMediator>();
            var inspection = mediator.ResolveQuery<IQueryKaskoInspection>().GetInspectionByDealEntityId(basket.GetKaskoDeal().EntityId);

            inspection.PostponedInspection = true;

            return inspection;
        }

        /// <summary>
        /// Установить версию справочников.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetDictionariyVersions(Basket basket)
        {
            if (basket == null)
            {
                return;
            }

            if (!basket.DictionaryDate.HasValue)
            {
                basket.DictionaryDate = DT.Now;
            }

            var dictionaryProcessor = new DictionaryProcessor();
            dictionaryProcessor.SetDictionariyVersions(ServiceProvider, basket, basket.DictionaryDate.Value);
        }

        /// <summary>
        /// Проверка возможности пропуска публикации корзины.
        /// </summary>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsAllowedSkipPublishBasket(string integrationLogin)
        {
            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.IsAllowedToSkipPublishBasket,
                KaskoEntitySettingCodes.IsAllowedToSkipPublishBasketDefault,
                KaskoEntitySettingCodes.IsAllowedToSkipPublishBasketByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false);
        }

        /// <summary>
        /// Проверка возможности пропуска публикации корзины на расчете.
        /// </summary>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsAllowedCalcSkipPublishBasket(string integrationLogin)
        {
            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.IsAllowedToSkipCalcPublishBasket,
                KaskoEntitySettingCodes.IsAllowedToSkipPublishBasketDefault,
                KaskoEntitySettingCodes.IsAllowedToSkipCalcPublishBasketByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false);
        }

        /// <summary>
        /// Проверка возможности использования дефолтного коэффициента SAS.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="integrationLogin">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckCanUseDefaultSasCoefficient(string integrationLogin)
        {
            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration<bool>(
                KaskoEntitySettingCodes.CanUseDefaultSasCoefficient,
                KaskoEntitySettingCodes.CanUseDefaultSasCoefficientDefault,
                KaskoEntitySettingCodes.CanUseDefaultSasCoefficientByIntegrationLogin,
                new IntegrationLogin { IntegrationLoginName = integrationLogin },
                false);
        }

        /// <summary>
        /// Проверка, необходим ли вызов SAS для получения коэффициента.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="integrationLoginName">Интеграционный логин.</param>
        /// <returns>Результат проверки.</returns>
        public bool CheckIsSasOnCalculateRequiredForLink2(Basket basket, string integrationLoginName)
        {
            if (basket.IsLink2Process != true || basket.GetKaskoDeal()?.IsSubscription() == true || IsLink2Upsale(basket) || basket.GetKaskoDeal()?.IsProlongation == true)
            {
                return false;
            }

            var settingsManager = (IEntitySettingsManager)ServiceProvider.GetRequiredService(typeof(IEntitySettingsManager));
            var queryServiceInvoker = (ICachedQueryServiceInvoker)ServiceProvider.GetRequiredService(typeof(ICachedQueryServiceInvoker));
            var integrationLogin = queryServiceInvoker.GetByExpression<IntegrationLogin>(x => x.Where(u => u.IntegrationLoginName == integrationLoginName)).FirstOrDefault();

            if (basket.IsLead == true)
            {
                settingsManager.TryGetSettingValue<string[]>(KaskoEntitySettingCodes.GetSasCoefficientOnCalculateForLead, out var partnersWithSasRequestForLead);

                if (!partnersWithSasRequestForLead.Contains(integrationLogin?.IntegrationLoginName))
                {
                    return false;
                }
            }

            var log = ServiceProvider.GetRequiredService<ILogger>().ForContext("BasketNumber", basket.Number);

            return settingsManager.GetSettingForDefaultOrPartnerConfiguration(
                KaskoEntitySettingCodes.GetSasCoefficientOnCalculate,
                KaskoEntitySettingCodes.GetSasCoefficientOnCalculateDefault,
                KaskoEntitySettingCodes.GetSasCoefficientOnCalculatePartner,
                integrationLogin,
                false,
                log);
        }

        /// <summary>
        /// Необходим ли пропуск всех проверок.
        /// </summary>
        /// <param name="basket">Корзина.</param>        
        /// <returns>Результат проверки.</returns>
        public bool AllKaskoChecksMustBeSkipped(Basket basket)
        {
            var deal = basket?.GetKaskoDeal();

            if (deal?.IsSubscription() == true)
            {
                return !string.IsNullOrEmpty(deal.AutoDeal.PreviousPolicyNumber);
            }

            var selectedOffer = deal?.GetSelectedProposal()?.Offer;
            if (selectedOffer == null)
            {
                return false;
            }

            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();
            if (settingsManager.TryGetSettingValue(
                KaskoSasCheckProcessConstants.SkipKaskoAllChecksSettingName,
                out List<int> specialOfferEntityIds))
            {
                return specialOfferEntityIds != null && specialOfferEntityIds.Contains(selectedOffer.EntityId);
            }

            return false;
        }

        /// <summary>
        /// Необходим ли пропуск ПСО для специальной кредитной машины.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="integrationLoginName">Интеграционный логин.</param>
        /// <returns>Признак необходимости пропуска ПСО.</returns>
        public bool PsoMustBeSkippedForSpecialCreditAuto(Basket basket, string integrationLoginName)
        {
            try
            {
                var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();
                var conditions = settingsManager.GetSettingValue<SpecialCreditAutoConditionsDto>("KaskoSpecialCrediAutoConditions");

                return SpecialCreditAutoPsoSkipHelper.IsCreditAutoWithSuitablePrice(conditions, basket)
                    && SpecialCreditAutoPsoSkipHelper.IsCorrectCreditBank(conditions, basket)
                    && SpecialCreditAutoPsoSkipHelper.HasCorrectFranchise(conditions, basket, ServiceProvider.GetService<IEntityIterator>())
                    && SpecialCreditAutoPsoSkipHelper.HasPreviousPolicy(conditions, basket)
                    && SpecialCreditAutoPsoSkipHelper.HasEntitySetting(integrationLoginName, settingsManager);
            }
            catch (Exception ex)
            {
                var log = (ILogger)ServiceProvider.GetService(typeof(ILogger));
                log.Error(ex, $"Ошибка при проверке возможности пропуска ПСО для кредитного автомобиля.");
            }

            return false;
        }

        /// <summary>
        /// Устанавливает подтверждение УРМ без выполнения проверок.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetFakeKaskoUrmApprove(Basket basket)
        {
            var kaskoDeal = basket.GetKaskoDeal();
            var urmApprovalCollection = kaskoDeal?.AutoDeal?.URMApproval;
            if (urmApprovalCollection != null && !urmApprovalCollection.Any())
            {
                kaskoDeal.AutoDeal.URMApproval.Add(new URMApproval
                {
                    ApprovalId = Guid.Empty.ToString(),
                    URMApprovalStatusCode = UrmApprovalStatusCodes.Accepted,
                    URMApprovalComment = $"Пропуск проверки УРМ/SAS."
                });
            }
            else if (urmApprovalCollection != null)
            {
                var actualApproval = UrmApprovalStatusHelper.GetActualApproval(kaskoDeal);
                actualApproval.URMApprovalComment = $"Пропуск проверки УРМ/SAS.";
                actualApproval.URMApprovalStatusCode = UrmApprovalStatusCodes.Accepted;
            }
        }

        private bool IsNotProlongation(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            return deal.IsProlongation != true;
        }

        public string GetBasketLink(Basket basket) => basket.KaskoBasketLink(ServiceProvider);

        public Dictionary<string, object> GetUserCreationMessageParameters(Basket basket)
        {
            return new Dictionary<string, object>
            {
                { "KaskoBasketLink", basket.KaskoBasketLink(ServiceProvider) },
            };
        }

        /// <summary>
        /// Фильтрация офферов Атисресса Каско согласно АБтестам.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void FilterAntistressOffersByABTestsByIris(Basket basket)
        {
            var config = ServiceProvider.GetRequiredService<IOptions<KaskoLink2ModuleConfiguration>>().Value;

            var abTestVariantOfferCodes = config.AbTestAntistressOffers.ABTestCodeVariants.Split(',')
                .Select((item, index) => new { Item = item, Index = index }).ToList();

            var abTestOfferToShow = GetOfferVariantByAbTest(config, abTestVariantOfferCodes.Select(v => v.Item).ToList(), basket);
            if (string.IsNullOrEmpty(abTestOfferToShow))
            {
                return;
            }

            // Id офферов для выбора
            var offerVariantIds = config.AbTestAntistressOffers.OfferIdVariants.Split(',').Select(s => int.Parse(s)).ToArray();

            // выбранный offerId
            var antistressOfferId = offerVariantIds[abTestVariantOfferCodes.Where(v => v.Item == abTestOfferToShow).First().Index];

            // Фильтрация оферов и сегментов
            var offersToExclude = offerVariantIds.Except(new int[] { antistressOfferId }).ToArray();

            ExcludeOffers(basket, offersToExclude);
        }

        /// <summary>
        /// Фильтрация офферов Каско согласно флагу isIrisCalculation.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="isIrisCalculation">Флаг способа расчёта</param>
        [Obsolete("Флаг isIrisCalculation удален. Для поддержки старых версий.")]
        public void FilterOffersByIsIrisCalculation(Basket basket, bool isIrisCalculation)
            => ExcludeOffers(basket, isIrisCalculation ? DiKaskoOfferIds.DiCalculationOfferIds : DiKaskoOfferIds.IrisCalculationOfferIds);

        /// <summary>
        /// Фильтрация офферов Каско.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void FilterOffers(Basket basket)
            => ExcludeOffers(basket, DiKaskoOfferIds.DiCalculationOfferIds);

        /// <summary>
        /// Исключение оферов из корзины и из сегментов.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="offersToExclude">Список Id оферов для исключения.</param>
        public void ExcludeOffers(Basket basket, IEnumerable<int> offersToExclude)
        {
            basket.Offers = basket.Offers.Where(o => !offersToExclude.Contains(o.Id)).ToList();

            foreach (var segment in basket.Segments)
            {
                segment.Offers = segment.Offers.Where(o => !offersToExclude.Contains(o.Id)).ToList();
            }
        }

        /// <summary>
        /// Фильтрация офферов Каско согласно выставленным условиям в запросе Link2.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void FilterOffersByLink2Conditions(Basket basket)
        {
            var link2FilterConditionsManager = ServiceProvider.GetRequiredService<IDiKaskoLink2FilterConditionsManager>();
            link2FilterConditionsManager.FilterOffers(basket);
        }

        /// <summary>
        /// Маппинг страховых сумм сделки Каско для расчета с тарификатором.
        /// </summary>
        /// <param name="deal">Сделка каско.</param>
        public void MapSumForIrisCalculation(Deal deal)
        {
            if (deal == null
                || deal.Product.ProductDictCode != ProductDictCodes.Kasko)
            {
                return;
            }

            var log = ServiceProvider.GetService<ILogger>();
            var context = ServiceProvider.GetService<PipelineExecutionContext>();

            log.ForContext("BasketNumber", context.GetBasketNumber())
                .Information("Маппинг страховых сумм сделки Каско для расчета с тарификатором.");

            var autoCost = deal.GetAuto().AutoCost.GetValueOrDefault();

            // словарь StructId - страховая умма
            var mappingSumDictionary = new Dictionary<int, decimal>()
            {
                [5031] = autoCost, // Угон/хищение
                [5004] = 10000, // Эвакуация
                [5005] = 2000, // Такси
                [5006] = 3000, // Тех. Помощь
                [5007] = 10000, // Автоконсьерж
                [5008] = 50000, // Отмена франшизы Каршеринга
                [5011] = 10000, // Аренда автомобиля
                [5012] = 10000, // Аварком 2 и более
                [5013] = 10000, // Аварком на все 
                [5036] = autoCost, // GAP страхование
                [5093] = autoCost, // ФД ремонт динамич
                [5092] = 500000 // ФД ремонт статич
            };

            bool funcMapper()
            {
                var dealHelper = new DealHelper(deal);

                //Если одновременно выбраны риски Ущерб (5016) и Ущерб только на условиях полная гибель (5029),
                //то записать Страховую сумму для Покрытия Ущерб (5015) = Стоимости автомобиля
                if (new List<int> { 5016, 5029 }.All(x => dealHelper.DealRisks.Exists(risk => risk.StructId == x)))
                {
                    var coverage = dealHelper.DealCoverages.FirstOrDefault(x => x.StructId == 5015);
                    if (coverage != null)
                    {
                        coverage.Sum = autoCost;
                    }
                }
/// <summary>
        /// Проверяет был ли выполнен перерасчет после изменения коэффициента УРМ.
        /// </summary>
        /// <returns>Значение ключа DiKaskoQuestionnaireProcessVariablesKeys.DiKaskoQuestionnaireStepRecalculationIsNeededAfterUrmChanged, если ключа нет, возварщает true.</returns>
        public bool IsRecalculationAfterUrmChangeDone(Basket basket)
        {
            var cache = ServiceProvider.GetService<ICache>();
            var key = DiKaskoQuestionnaireProcessVariablesKeys.DiKaskoQuestionnaireStepRecalculationIsNeededAfterUrmChanged;
            var needToRecalculate = cache.Get<bool?>($"{key}_{basket.Number}");
            if (needToRecalculate.HasValue && basket.HasKasko())
            {
                return !needToRecalculate.Value;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// Логирование ссылки на оплату.
        /// </summary>
        /// <param name="paymentUrl">Ссылка на оплату.</param>
        public void LogPaymentLink(string paymentUrl)
        {
            var logger = ServiceProvider.GetService<ILogger>();
            logger.ForContext<DiKaskoInsuranceProcedures>().ForContext("PaymentLink", paymentUrl).Information($"Ссылка на оплату {paymentUrl}");
        }

        /// <summary>
        /// Маппинг ответа на запрос КБМ в корзину.
        /// </summary>
        public void MapKbmAndToResponse(Basket basket, KbmAndTOResponse kbmAndToResponse)
        {
            var responseMapper = ServiceProvider.GetRequiredService<IKbmResponseMapper>();

            responseMapper.Map(kbmAndToResponse.KbmResult, basket);
        }

        /// <summary>
        /// Установка дефолтных значений КБМ.
        /// </summary>
        public void SetDefaultKbm(Basket basket)
        {
            var kbmclassProvider = ServiceProvider.GetRequiredService<IKbmClassProvider>();
            var defaultKbmInitializer = new DefaultKbmInitializer(kbmclassProvider);

            defaultKbmInitializer.SetDefaultKbmTo(basket);
        }

        /// <summary>
        /// Простановка признака отправки в CDP файловым блокам, специфичным для каско линк2.
        /// </summary>
        public void SetExternalSystemTypeToDocs(Basket basket, bool isProcessApiRequest)
        {
            var deal = basket.GetKaskoDeal();
            if (deal != null)
            {
                List<FileBlock> allFileBlocks = new List<FileBlock>();

                allFileBlocks.Add(deal.OtherInsuranceCompanyPolicy?.FileBlock);
                allFileBlocks.AddRange(deal.AutoDeal.Drivers?.Select(driver => driver.DriverLicenseScan));
                allFileBlocks.Add(deal.AutoDeal.TsIdentityCard?.FileBlock);
                allFileBlocks.Add(deal.AutoDeal.TsIdentityCard?.OwnershipProof);
                allFileBlocks.Add(deal.GetAuto().DiagnosticCard?.FileBlock);
                allFileBlocks.Add(deal.GetOwner().IdentityDocument?.FileBlock);
                allFileBlocks.Add(deal.Insurant.IdentityDocument?.FileBlock);
                allFileBlocks.Add(deal.AutoDeal?.InspectionDocument?.OptEquipmentPsoDoc);
                allFileBlocks.Add(deal.AutoDeal?.InspectionDocument?.DamagePsoDoc);
                allFileBlocks.Add(deal.AutoDeal?.InspectionDocument?.TraditionalPsoDoc);

                SetCdpSystemType(allFileBlocks);
            }

            void SetCdpSystemType(List<FileBlock> fileBlocks)
            {
                foreach (var fileBlock in fileBlocks)
                {
                    if (fileBlock != null)
                    {
                        fileBlock.FileGroups.ForEach(fileGroup =>
                        {
                            fileGroup.FileInfos.ForEach(fileInfo =>
                            {
                                fileInfo.ExternalSystemTypeCode = ExternalSystemTypeCodes.Cdp;
                            });
                        });
                    }
                }
            }
        }

        public void SetCheckNecessityCode(Basket basket, bool isProcessApiRequest)
        {
            var deal = basket.GetKaskoDeal();
            if (deal != null && isProcessApiRequest)
            {
                deal.InsuranceObjects.ForEach(io =>
                {
                    io.CheckNecessityCode = PsoInspectCodes.SkipPsoCheckNecessityCode;
                });
            }
        }

        /// <summary>
        /// Установка значения поправочного коэффициента K_Underwriter и ID его согласования.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <param name="correctionCoef">Значение коэфиициента.</param>
        /// <param name="agreementId">ID согласования.</param>
        public void SetCorrectionCoefficient(Basket basket, string correctionCoef, string agreementId)
        {
            if (basket.UpsaleSourceBasketNumber == string.Empty || basket.UpsaleSourceBasketNumber == null)
            {
                var deal = basket.GetKaskoDeal();
                if (!string.IsNullOrEmpty(correctionCoef))
                {
                    foreach (var proposal in deal.Proposals)
                    {
                        var selectedDiscountCombination = proposal.DiscountCombinations?.SingleOrDefault(x => x.IsSelected == true);
                        var dealDiscount = selectedDiscountCombination?.Discounts?.SingleOrDefault(d =>
                            d.CalculationComponentCoefficient.Coefficient.Name.Equals(DiscountCoefficientNames.K_Underwriter, StringComparison.OrdinalIgnoreCase));

                        if (dealDiscount != null)
                        {
                            dealDiscount.DiscountSwitchOnOff = true;
                            dealDiscount.DiscountValue = decimal.Parse(correctionCoef);
                        }
                    }
                }

                if (!string.IsNullOrEmpty(agreementId))
                {
                    var approval = deal.AutoDeal.DIUnderwriterApprovals.SingleOrDefault(d => d.ThemeCode == DIUnderwriterApprovalThemeCodes.CorrectCoeff);
                    if (approval != null)
                    {
                        approval.ApprovalId = agreementId;
                    }
                    else
                    {
                        deal.AutoDeal.DIUnderwriterApprovals.Add(new DIUnderwriterApproval
                        {
                            ApprovalId = agreementId,
                            ThemeCode = DIUnderwriterApprovalThemeCodes.CorrectCoeff,
                            StatusCode = DiUnderwriterApprovalStatusCodes.NotRequired,
                            UniqueId = Guid.NewGuid(),
                            IsClosed = true
                        });
                    }
                }
            }
        }

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

        [Obsolete("Удалить после апреля 2024го года")]
        public string GetAuthLink(Basket basket)
        {
            return SecurityUrlBuilder.BuildLoginUrl(ServiceProvider);
        }

        [Obsolete("Удалить после апреля 2024го года")]
        public Dictionary<string, object> GetRegistrationMessageParametersRusfinancebank(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var dateStart = deal.DateStart.Value;

            //оплата в течение недели
            var date = GetWeekLaterFromNow();

            if (!CheckIsRusfinancebank(basket))
            {
                return null;
            }

            var url = GetBasketLink(basket);

            if (date >= dateStart)
            {
                //за день до начала действия полиса
                date = GetYesterdayDate(dateStart);
            }

            var basketUsefulDate = date.ToString("dd.MM.yyyy");

            return new Dictionary<string, object>
            {
                { "BasketNumber", basket.Number },
                { "BasketLink", url },
                { "BasketUsefulDate", basketUsefulDate },
            };
        }

        [Obsolete("Удалить после апреля 2024го года")]
        public Dictionary<string, string> GetMessageParametersRusfinancebank(Basket basket, string login)
        {
            var parameters = GetRegistrationMessageParametersRusfinancebank(basket).ToDictionary(x => x.Key, x => x.Value.ToString());

            parameters.Add("Login", login);
            parameters.Add("Link", SecurityUrlBuilder.BuildLoginUrl(ServiceProvider));

            return parameters;
        }

        [Obsolete("Удалить после апреля 2024го года")]
        public string GetMessageTemplateCode(Basket basket)
        {
            return CheckIsRusfinancebank(basket) ? TemplateLoginAndPasswordRusfinancebankName : null;
        }

        /// <summary>
        /// Изменить тип корзины и удалить сделку EOsago из корзины.
        /// </summary>
        /// <param name="basket">корзина.</param>
        [Obsolete("Удалить после апреля 2024го года")]
        public void DeleteOsagoInsuranceTypeFromBasket(Basket basket)
        {
            var osagoInsurance = basket.InsuranceTypes.Single(i => i.Code == "OSAGO");
            basket.InsuranceTypes.Remove(osagoInsurance);
            basket.GetOsagoDeal().IsInBasket = false;
        }

        /// <summary>
        /// Изменить тип корзины и добавить сделку EOsago в корзину.
        /// </summary>
        /// <param name="basket">корзина.</param>
        [Obsolete("Удалить после апреля 2024го года")]
        public void AddOsagoInsuranceTypeToBasket(Basket basket)
        {
            if (basket.InsuranceTypes.Any(i => i.Code == "OSAGO"))
            {
                return;
            }

            var cache = ServiceProvider.GetRequiredService<ICache>();
            var context = ServiceProvider.GetRequiredService<PipelineExecutionContext>();
            var reactiveMemoryCache = ServiceProvider.GetRequiredService<IReactiveMemoryCache>();
            var mediator = ServiceProvider.GetRequiredService<ICqrsMediator>();
            var osagoInsuranceType = mediator.ResolveQuery<IQueryInsuranceType>().GetInsuranceTypes(new List<string> { DiKaskoInsuranceTypeCodes.Osago }).Single();
            basket.InsuranceTypes.Add(osagoInsuranceType);

            var osago = basket.GetOsagoDeal();
            if (osago != null)
            {
                osago.IsInBasket = true;
                osago.IsAccurateCalculation = osago.AutoDeal.Drivers.Any(d => !string.IsNullOrWhiteSpace(d.DriverLicenseNumber));
            }

            var contextStorage = new SegmentationContextStorage($"{nameof(SegmentationContext)}_{basket.Number}", cache);
            {
                var segmentOfferDeterminator = ServiceProvider.GetService<ISegmentOfferDeterminator>();
                var segmentWrappers = segmentOfferDeterminator.DetermineSegments(basket);

                segmentWrappers = segmentWrappers
                    .Where(sw => basket.Segments.Any(s => s.Id == sw.Segment.Id)
                    || sw.Segment.Product.InsuranceTypeCode == DiKaskoInsuranceTypeCodes.Osago)
                    .ToList();

                var segmentationContext = contextStorage.GetContext();
                var worker = new SegmentationWorker(
                    segmentationContext,
                    segmentWrappers,
                    ServiceProvider.GetService<IExpressionFunctionProvider>(),
                    ServiceProvider.GetService<IQuerySegment>(),
                    ServiceProvider.GetService<ILogger>());

                worker.AddInsuranceFilter(basket.InsuranceTypes.Select(it => it.Code).ToArray());
                contextStorage.Commit();
            }

            foreach (var insuranceType in basket.InsuranceTypes)
            {
                var product = reactiveMemoryCache.GetOrCreateInCache(
                    $"Segmentation_GetProductForInsuranceType_{insuranceType.Code}",
                    () => mediator.ResolveQuery<IQueryProduct>().GetProductForInsuranceType(insuranceType.Code, basket.ExternalTagList?.UseDrafts));

                if (product != null && !basket.Products.Any(p => p.Id == product.Id))
                {
                    basket.Products.Add(product);
                }
            }
        }

        /// <summary>
        /// Получить список оплаченных сделок.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        /// <returns>Список оплаченных сделок.</returns>
        [Obsolete("Удалить после апреля 2024го года")]
        public List<Deal> GetPaidDeals(Basket basket)
        {
            var kasko = basket.GetKaskoDeal();
            var isPaidKasko = kasko != null && kasko.IsInBasket == true
                && basket.Payments.Any(p => p.Invoices.Any(i => kasko.Invoices.Any(di => di.Id == i.Id))
                    && PaymentStatusCodes.PaymentAttemptedStatuses.Contains(p.StatusCode));

            var eosago = basket.GetEOsagoDealInBasket();
            var isPaidEOsago = eosago != null && eosago.DealStatusCode.InEnum(EntityStatusType.Signed, EntityStatusType.PolicyIssued);

            return new[]
            {
                isPaidKasko ? kasko : null,
                isPaidEOsago ? eosago : null
            }
            .Where(d => d != null).ToList();
        }

        #endregion

        /// <summary>
        /// Маппинг запроса КБМ в РСА.
        /// </summary>
        public KbmAndTOIntegrRequest MapKbmAndToRequest(Basket basket)
        {
            var requestMapper = ServiceProvider.GetRequiredService<IKbmRequestMapper>();

            return requestMapper.Map(basket);
        }

        public List<string> GetIntermediaryEmail(Basket basket)
        {
            var subject = basket.GetKaskoDeal().GetMainIntermediary().Subject;

            if (subject.LegalEntity_Id != null)
            {
                var queryServiceInvoker = ServiceProvider.GetRequiredService<ICachedQueryServiceInvoker>();
                var legalEntity = queryServiceInvoker.GetById<LegalEntity>(subject.LegalEntity_Id.Value, "LegalEntityDetailed");

                return new List<string> { legalEntity?.Email1 };
            }
            else
            {
                return new List<string> { subject.IndividualEntity?.Person?.Email1 };
            }
        }

        /// <summary>
        /// Установка статуса УРМ "В работе".
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetURMApprovalStatusInWork(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastUrmApproval(out var approval) &&
                (approval.URMApprovalStatusCode == UrmApprovalStatusCodes.Required ||
                 approval.URMApprovalStatusCode == UrmApprovalStatusCodes.RequiredExtraInfo))
            {
                approval.URMApprovalStatusCode = UrmApprovalStatusCodes.InWork;
            }
        }

        /// <summary>
        /// Установка статуса проверки ОТА "В работе".
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetOtaApprovalStatusInWork(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval) &&
                (approval.OtaApprovalStatusCode == OtaApprovalStatusCodes.Required ||
                 approval.OtaApprovalStatusCode == OtaApprovalStatusCodes.Mistakes))
            {
                approval.OtaApprovalStatusCode = OtaApprovalStatusCodes.InWork;
            }
        }

        /// <summary>
        /// Установка признака повторного запроса ОТА.
        /// </summary>
        /// <param name="basket">Корзина.</param>
        public void SetOtaApprovalIsRepeated(Basket basket)
        {
            if (basket.GetKaskoDeal().AutoDeal.TryGetLastOtaApproval(out var approval))
            {
                approval.IsRepeated = true;
            }
        }
//только в случае если нет Риска Ущерб (5016) и есть Ущерб только на условиях полная гибель (5029)
                if (dealHelper.DealRisks.Exists(x => x.StructId == 5029) &&
                    !dealHelper.DealRisks.Exists(x => x.StructId == 5016))
                {
                    dealHelper.DealRisks.First(x => x.StructId == 5029).Sum = autoCost;
                }

                // если в сделке есть риск "Ущерб. ДТП со 2 участником"
                var dmgSecPartRisk = dealHelper.DealRisks.SingleOrDefault(x => x.ProductRisk.RiskCode == RiskCodes.DMGSECPART);
                if (dmgSecPartRisk != null)
                {
                    dmgSecPartRisk.Sum = autoCost;
                    if (dealHelper.DealOptions.Any(x => x.ProductOption.OptionCode == OptionCodes.FDREPAIRFIX))
                    {
                        dmgSecPartRisk.Sum = 500000m;
                    }
                }

                foreach (var mappingSum in mappingSumDictionary)
                {
                    var option = dealHelper.DealOptions.FirstOrDefault(x => x.StructId == mappingSum.Key);
                    if (option != null)
                    {
                        option.Sum = mappingSum.Value;
                        continue;
                    }

                    var risk = dealHelper.DealRisks.FirstOrDefault(x => x.StructId == mappingSum.Key);
                    if (risk != null)
                    {
                        risk.Sum = mappingSum.Value;
                        continue;
                    }

                    var coverage = dealHelper.DealCoverages.FirstOrDefault(x => x.StructId == mappingSum.Key);
                    if (coverage != null)
                    {
                        coverage.Sum = mappingSum.Value;
                    }
                }

                return true;
            }

            bool ossMapper()
            {
                var dealCoverages = new DealHelper(deal).DealCoverages;
                foreach (var coverage in dealCoverages)
                {
                    var franchisesWithPercentInsuranceSum = coverage.DealRisks
                       .Where(risk => risk.ConditionBlock != null)
                       .SelectMany(risk => risk.ConditionBlock.DealFranchises
                              .Where(x => x.ProductFranchise.SetSums
                                   .Where(y => y.PercentInsuranceSum != null).Any()));

                    foreach (var dealRisk in coverage.DealRisks.Where(d => d.Sum.HasValue))
                    {
                        coverage.Sum = dealRisk.Sum;

                        var dealFranchises = dealRisk.ConditionBlock?.DealFranchises?.Where(x =>
                        x != null
                        && x.ProductFranchise != null
                        && x.ProductFranchise.SetSums != null
                        && x.ProductFranchise.SetSums
                                   .Where(y => y != null && y.PercentInsuranceSum != null).Any());

                        if (dealFranchises != null)
                        {
                            foreach (var franchise in dealFranchises)
                            {
                                franchise.Sum = dealRisk.Sum * franchise.ProductFranchise.SetSums.Single().PercentInsuranceSum.Value;
                            }
                        }
                    }

                    if (coverage.ProductCoverage.CoverageCode.Equals(CoverageCodes.ADDCOSTS, StringComparison.OrdinalIgnoreCase))
                    {
                        var options = coverage.DealRisks
                            .Select(dr => dr.ConditionBlock)
                            .SelectMany(x => x.DealOptions)
                            .Where(x => x != null)
                            .GroupBy(o => o.Id).Select(g => g.First())
                            .Where(d => d.Sum.HasValue);

                        coverage.Sum = options.Sum(o => o.Sum.Value);
                    }
                }

                return true;
            }

            TryDoSomethingAndWriteLogIfItsCrash(funcMapper, "Ошибка маппинга страховых сумм сделки Каско для расчета с тарификатором.");
            TryDoSomethingAndWriteLogIfItsCrash(ossMapper, "Ошибка маппинга для ОСС.");
        }

        public int GetSelectedOfferId(Deal deal)
        {
            var proposal = deal.GetSelectedProposal();
            return proposal.Offer_Id.Value;
        }

        public List<PsoInspectionFileDescriptionModel> CreateInspectionFileDescriptionModel(Basket basket)
        {
            var deal = basket.GetKaskoDeal();
            var queryPrintingStores = ServiceProvider.GetService<IQueryPrintingStores>();
            var printingStores = queryPrintingStores.GetPrintStoresForDeal(deal);

            var document = new PrintingActivityHelper().PrintDocument(ServiceProvider, printingStores, basket, false, null);

            var result = new List<PsoInspectionFileDescriptionModel>
            {
                new PsoInspectionFileDescriptionModel()
                {
                    FileContent = document,
                    FileName = printingStores.First(t => t.PrintingOrder == printingStores.Min(subt => subt.PrintingOrder)).PrintingGroupNameRu,
                    FileExtension = "pdf"
                }
            };

            if (TryGetSettingsForIntermediary<bool>(deal, "KaskoUploadOtherInsuranceCompanyPolicyDocumentsToPso"))
            {
                result.AddRange(DownloadOtherInsuranceCompanyPolicyDocuments(deal).Select(x => new PsoInspectionFileDescriptionModel()
                {
                    FileContent = x.Content,
                    FileName = $"Предыдущий полис {Path.GetFileNameWithoutExtension(x.FileName)}",
                    FileExtension = Path.GetExtension(x.FileName)?.Replace(".", string.Empty)
                }));
            }

            return result;
        }

        internal T TryGetSettingsForIntermediary<T>(Deal deal, string settingsKey, T defaultValue = default)
        {
            var intermediary = deal.GetMainIntermediary();

            var settingsManager = ServiceProvider.GetRequiredService<IEntitySettingsManager>();
            if (settingsManager.TryGetSettingValue<T>(settingsKey, intermediary, out var settingsValue))
            {
                return settingsValue;
            }

            return defaultValue;
        }

        internal List<(string FileName, byte[] Content)> DownloadOtherInsuranceCompanyPolicyDocuments(Deal deal)
        {
            var documents = new List<(string FileName, byte[] Content)>();
            var logger = ServiceProvider.GetService<ILogger>();
            var fileStorageServiceClient = ServiceProvider.GetService<IFileStorageServiceClient>();

            if (deal.OtherInsuranceCompanyPolicy?.FileBlock?.FileGroups?.Any(fg => fg.FileInfos != null && fg.FileInfos.Any(fi => fi.FileGuid != null)) == true)
            {
                foreach (var fileInfo in deal.OtherInsuranceCompanyPolicy.FileBlock.FileGroups.SelectMany(x => x.FileInfos))
                {
                    if (fileInfo.FileGuid == null)
                    {
                        continue;
                    }

                    byte[] documentData = null;
                    try
                    {
                        documentData = fileStorageServiceClient.DownloadFileContent(fileInfo.FileGuid.ToString());
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex, $"Ошибка скачивания файла {fileInfo.FileName} {fileInfo.FileGuid} предыдущего полиса для отправки в ПСО.");
                        continue;
                    }

                    if (documentData != null)
                    {
                        documents.Add((fileInfo.FileName, documentData));
                    }
                }
            }

            return documents;
        }

        /// <summary>
        /// Устанавливаем текст особых условий для сделки каско
        /// https://jira.renins.com/browse/IRIS-98443. Перенести в маппинг ОСС KaskoOssMapper.GetSpecialNotes.
        /// </summary>
        /// <param name="deal"></param>
        public void SetSpecialNotes(Deal deal)
        {
            deal.SpecialNotes = GetSpecialNotes(deal);
        }

        /// <summary>
        /// возвращает текст особых условий для сделки каско
        /// </summary>
        /// <param name="deal"></param>
        public string GetSpecialNotes(Deal deal)
        {
            if (deal?.Product?.ProductDictCode != ProductDictCodes.Kasko)
            {
                return null;
            }

            var scManager = ServiceProvider.GetRequiredService<ISpecialClausesBankManager>();

            var sb = new StringBuilder();
            var specialConditions = scManager.GetSpecialClauses(deal);

            if (!string.IsNullOrEmpty(specialConditions))
            {
                sb.Append(specialConditions);
                sb.Append(Environment.NewLine);
            }

            sb.Append("При отказе Страхователя от Полиса до даты начала срока страхования, Страховщик возвращает Страхователю страховую премию в полном объеме. При отказе Страхователя от Полиса после даты начала срока страхования и до 14 календарного дня с даты заключения Полиса включительно, Страхователь имеет право получить часть уплаченной страховой премии, рассчитанной пропорционально сроку действия страхования. Возврат уплаченной страховой премии осуществляется Страхователю при отсутствии в период страхования событий с признаками страхового случая. Денежные средства возвращаются Страхователю в течение десяти рабочих дней с даты получения Страховщиком заявления Страхователя об отказе от Договора. При отказе Страхователя от Полиса в иных случаях применяется пункт 7.8.3 Правил.");
            if (KaskoOsagoPrintingMapperFunctions.HasRisk(deal, RiskCodes.CARJACKING))
            {
                sb.Append(Environment.NewLine);
                sb.Append(
                    "Страховщик на свое усмотрение имеет право осуществить страховую выплату по риску «Угон/Хищение» до предоставления Страхователем (Выгодоприобретателем, Застрахованными лицами) Постановления о прекращении уголовного дела.");
            }

            if (deal.InsuranceObjects[0].CheckNecessityCode != CheckNecessityCodes.InspecYes)
            {
                sb.Append("Принимая настоящий Полис и оплачивая страховую премию, Страхователь подтверждает, что застрахованное ТС является новым, ранее не эксплуатировалось и на дату начала срока страхования находится в салоне официального дилера. Дата передачи ТС Страхователю (дата начала эксплуатации ТС), устанавливаемая на основании договора купли-продажи, акта приема-передачи ТС или отметки в " + deal.AutoDeal.TsIdentityCard.TsIdentityCardType.Name + ", должна совпадать или быть позже даты начала срока страхования по настоящему Полису. В ином случае Договор страхования считается не вступившим в силу и действие срока страхования не начинается до подписания между Страховщиком и Страхователем Акта осмотра ТС.");
                sb.Append(Environment.NewLine);
            }

            sb.Append("Правила страхования");

            if (deal.InsuranceObjects[0].CheckNecessityCode != CheckNecessityCodes.InspecYes)
            {
                sb.Append(", Акт осмотра ТС");
            }

            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();
        }
напиши полный путь до файлового блока