< Summary

Information
Class: Services.ApiServices.InterpartsPriceApi.InterpartsPriceOfferConverter
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/InterpartsPriceApi/InterpartsPriceOfferConverter.cs
Line coverage
90%
Covered lines: 96
Uncovered lines: 10
Coverable lines: 106
Total lines: 209
Line coverage: 90.5%
Branch coverage
61%
Covered branches: 44
Total branches: 72
Branch coverage: 61.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/ApiServices/InterpartsPriceApi/InterpartsPriceOfferConverter.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using System.Globalization;
 3using Services.ApiServices.InterpartsPriceApi.Models;
 4using Services.ApiServices.TradesoftApi.Models.Response;
 5
 6namespace Services.ApiServices.InterpartsPriceApi;
 7
 8public static class InterpartsPriceOfferConverter
 9{
 10    /// <summary>
 11    /// Id валюты справочника Interparts, по курсу которой пересчитываются цены ответа API: «RUR (б/н)».
 12    /// Совпадает с валютой по умолчанию в окне поиска Interparts, поэтому итоги сходятся один в один.
 13    /// </summary>
 14    private const int AppliedRateCurrencyId = 1;
 15
 16    /// <summary>Валюта цен в ответе price-api — доллары.</summary>
 17    private const string SourceCurrencyCode = "USD";
 18
 119    private static readonly IReadOnlyDictionary<int, decimal> EmptyRates =
 120        new ReadOnlyDictionary<int, decimal>(new Dictionary<int, decimal>());
 21
 122    private static readonly IReadOnlyDictionary<int, string> EmptyCurrencyNames =
 123        new ReadOnlyDictionary<int, string>(new Dictionary<int, string>());
 24
 25    public static List<PreOrderItem> ConvertToPreOrderItems(
 26        IEnumerable<InterpartsPriceOffer>? sourceOffers,
 27        string? requestLogin,
 28        IReadOnlyDictionary<int, decimal>? ratesToRub,
 29        IReadOnlyDictionary<int, string>? currencyNames = null)
 30    {
 431        if (sourceOffers == null)
 132            return new List<PreOrderItem>();
 33
 334        var rates = ratesToRub ?? EmptyRates;
 35
 336        return sourceOffers
 337            .Where(o => o != null)
 238            .Select(o => ConvertSingleItem(o, requestLogin, rates, currencyNames))
 239            .Where(i => i != null)
 340            .ToList()!;
 41    }
 42
 43    public static PreOrderItem? ConvertSingleItem(
 44        InterpartsPriceOffer? offer,
 45        string? requestLogin,
 46        IReadOnlyDictionary<int, decimal>? ratesToRub,
 47        IReadOnlyDictionary<int, string>? currencyNames = null)
 48    {
 2549        var rates = ratesToRub ?? EmptyRates;
 50
 2551        if (offer == null)
 152            return null;
 53
 2454        if (offer.IsPriceActual != true)
 255            return null;
 56
 2257        if (offer.Amount <= 0)
 358            return null;
 59
 1960        var supplierPriceString = BuildSupplierPurchasePriceString(offer, rates);
 1961        var originalCurrencyCode = SourceCurrencyCode;
 1962        var originalPriceString = offer.OurPrice2Decimal.HasValue
 1963            ? offer.OurPrice2Decimal.Value.ToString("0.##", CultureInfo.InvariantCulture)
 1964            : "0";
 65
 1966        var currencyCode = "RUB";
 1967        var itemId = string.IsNullOrWhiteSpace(offer.PartCodeShort) ? offer.PartCode : offer.PartCodeShort;
 1968        var effectiveLogin = string.IsNullOrWhiteSpace(requestLogin) ? "admin" : requestLogin.Trim();
 69
 1970        var supplierName = offer.SupplierName ?? string.Empty;
 1971        var partCodeShort = offer.PartCodeShort ?? offer.PartCode ?? string.Empty;
 1972        var direction = offer.SupplierExtId ?? string.Empty;
 73
 1974        if (direction.Contains('-'))
 175            return null;
 76
 1877        direction += "_" + offer.SupplyTypeExtId ?? string.Empty;
 78
 1879        var costPriceString = TryBuildCostPriceStringFromInputPrice(offer, rates);
 1880        var priceDescription = BuildPriceDescription(offer, rates, currencyNames);
 81
 1882        return new PreOrderItem
 1883        {
 1884            Caption = offer.PartDescription,
 1885            Rest = (offer.Amount ?? 0).ToString(CultureInfo.InvariantCulture),
 1886            DeliveryDays = (offer.Term ?? 0).ToString(CultureInfo.InvariantCulture),
 1887            DeliveryDaysMin = offer.Term ?? 0,
 1888            DeliveryDaysMax = offer.Term ?? 0,
 1889            CurrencyCode = currencyCode,
 1890            Producer = offer.ProducerName ?? string.Empty,
 1891            Code = partCodeShort,
 1892            Price =  costPriceString,
 1893            CostPrice = supplierPriceString,
 1894            Direction = direction,
 1895            ItemHash = $"{partCodeShort}_{supplierName}".GetHashCode().ToString("X"),
 1896            ItemId = itemId ?? string.Empty,
 1897            MinQuantity = offer.Multiplicity ?? 1,
 1898            SiteUrl = "http://studio-api.interparts.ru/",
 1899            Title = supplierName,
 18100            PriceDescription = priceDescription,
 18101            Description =
 18102                $"Поставщик: {supplierName}\n" +
 18103                $"Артикул: {partCodeShort}\n" +
 18104                $"Цена поставщика: {supplierPriceString} {currencyCode}\n" +
 18105                (costPriceString != null ? $"Себестоимость (вход): {costPriceString} {currencyCode}\n" : string.Empty) +
 18106                $"Цена в оригинальной валюте (поставщик): {originalPriceString} {originalCurrencyCode}\n" +
 18107                $"SupplierCurrencyId: {offer.SupplierCurrencyId}\n" +
 18108                $"Логин запроса: {effectiveLogin}\n" +
 18109                $"Время обновления: {offer.UpdatedTime}\n"
 18110        };
 111    }
 112
 113    private static string BuildSupplierPurchasePriceString(
 114        InterpartsPriceOffer offer,
 115        IReadOnlyDictionary<int, decimal> rates)
 116    {
 19117        var rub = ConvertSupplierPriceToRub(offer.OurPrice2Decimal, AppliedRateCurrencyId, rates).RubPrice;
 19118        return FormatRoundedRubString(rub);
 119    }
 120
 121    private static string? TryBuildCostPriceStringFromInputPrice(
 122        InterpartsPriceOffer offer,
 123        IReadOnlyDictionary<int, decimal> rates)
 124    {
 18125        if (!offer.InputPriceDecimal.HasValue)
 16126            return null;
 127
 2128        var rub = ConvertSupplierPriceToRub(offer.InputPriceDecimal, AppliedRateCurrencyId, rates).RubPrice;
 2129        return FormatRoundedRubString(rub);
 130    }
 131
 132    private static string FormatRoundedRubString(double? rubPrice)
 133    {
 41134        if (!rubPrice.HasValue)
 0135            return "0";
 136
 41137        return Math.Round(rubPrice.Value, MidpointRounding.AwayFromZero)
 41138            .ToString("0", CultureInfo.InvariantCulture);
 139    }
 140
 141    private static (double? RubPrice, bool UsedFallbackRate) ConvertSupplierPriceToRub(
 142        double? sourcePrice,
 143        int? supplierCurrencyId,
 144        IReadOnlyDictionary<int, decimal> rates)
 145    {
 21146        if (!sourcePrice.HasValue)
 0147            return (0, false);
 148
 21149        var rate = InterpartsCurrencyRateService.GetRateToRub(AppliedRateCurrencyId, rates, out var usedFallback);
 150
 21151        return (sourcePrice.Value * (double)rate, usedFallback);
 152    }
 153
 154    /// <summary>
 155    /// Расшифровка пересчёта для подсказки в проценке: цена ответа API, применённый курс и результат в рублях.
 156    /// Точка с запятой — разделитель строк подсказки на фронте.
 157    /// </summary>
 158    private static string BuildPriceDescription(
 159        InterpartsPriceOffer offer,
 160        IReadOnlyDictionary<int, decimal> rates,
 161        IReadOnlyDictionary<int, string>? currencyNames)
 162    {
 18163        var rate = InterpartsCurrencyRateService.GetRateToRub(AppliedRateCurrencyId, rates, out var usedFallback);
 18164        var names = currencyNames ?? EmptyCurrencyNames;
 18165        var rateName = names.TryGetValue(AppliedRateCurrencyId, out var name) && !string.IsNullOrWhiteSpace(name)
 18166            ? name
 18167            : $"id {AppliedRateCurrencyId}";
 168
 18169        var lines = new List<string>();
 170
 18171        if (offer.InputPriceDecimal.HasValue)
 172        {
 2173            lines.Add(
 2174                $"Цена: {FormatSourceValue(offer.InputPriceDecimal.Value)} {SourceCurrencyCode} × {FormatRate(rate)} = "
 2175                $"{FormatRoundedRubString(offer.InputPriceDecimal.Value * (double)rate)} RUB");
 176        }
 177
 18178        if (offer.OurPrice2Decimal.HasValue)
 179        {
 18180            lines.Add(
 18181                $"Себест.: {FormatSourceValue(offer.OurPrice2Decimal.Value)} {SourceCurrencyCode} × {FormatRate(rate)} =
 18182                $"{FormatRoundedRubString(offer.OurPrice2Decimal.Value * (double)rate)} RUB");
 183        }
 184
 18185        lines.Add(usedFallback
 18186            ? $"Курс не найден в справочнике Interparts (id {AppliedRateCurrencyId}), применён 1"
 18187            : $"Курс: справочник Interparts «{rateName}» (id {AppliedRateCurrencyId}) = {FormatRate(rate)}");
 188
 18189        return string.Join("; ", lines);
 190    }
 191
 192    private static string FormatSourceValue(double value) =>
 20193        value.ToString("0.####", CultureInfo.InvariantCulture);
 194
 195    private static string FormatRate(decimal rate) =>
 25196        rate.ToString("0.####", CultureInfo.InvariantCulture);
 197
 198    private static string MapSupplierCurrencyIdToCode(int? supplierCurrencyId)
 199    {
 0200        return supplierCurrencyId switch
 0201        {
 0202            1 or 14 or 15 => "RUB",
 0203            3 or 7 => "USD",
 0204            5 or 13 => "EUR",
 0205            2 or 20 or 21 => "BYN",
 0206            _ => "UNKNOWN"
 0207        };
 208    }
 209}

Methods/Properties

.cctor()
ConvertToPreOrderItems(System.Collections.Generic.IEnumerable`1<Services.ApiServices.InterpartsPriceApi.Models.InterpartsPriceOffer>,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.String>)
ConvertSingleItem(Services.ApiServices.InterpartsPriceApi.Models.InterpartsPriceOffer,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.String>)
BuildSupplierPurchasePriceString(Services.ApiServices.InterpartsPriceApi.Models.InterpartsPriceOffer,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>)
TryBuildCostPriceStringFromInputPrice(Services.ApiServices.InterpartsPriceApi.Models.InterpartsPriceOffer,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>)
FormatRoundedRubString(System.Nullable`1<System.Double>)
ConvertSupplierPriceToRub(System.Nullable`1<System.Double>,System.Nullable`1<System.Int32>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>)
BuildPriceDescription(Services.ApiServices.InterpartsPriceApi.Models.InterpartsPriceOffer,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.Decimal>,System.Collections.Generic.IReadOnlyDictionary`2<System.Int32,System.String>)
FormatSourceValue(System.Double)
FormatRate(System.Decimal)
MapSupplierCurrencyIdToCode(System.Nullable`1<System.Int32>)