using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Электронная_Фармация.Properties; namespace Электронная_Фармация.Classes { /// /// Собирает доступные скидки покупателя/прайса/региона и вешает на позицию то, что пришло. /// Отсутствующий источник = 0. /// public static class DiscountResolver { public static async Task LoadAsync(ApiClient client, string loginHint) { var lookup = new DiscountLookup(); if (client == null) { return lookup; } var regionId = (Settings.Default.RegionId ?? string.Empty).Trim(); lookup.RegionId = regionId; string buyerId = null; try { var buyers = await client.GetBuyersAsync(); buyerId = ResolveBuyerId(buyers, loginHint); lookup.BuyerId = buyerId; AppDebugLog.Info( "Discount", string.IsNullOrWhiteSpace(buyerId) ? "buyer_id не найден — клиентская скидка будет 0" : $"buyer_id={buyerId}"); } catch (Exception ex) { AppDebugLog.Warning("Discount", $"Не удалось загрузить покупателей: {ex.Message}"); } List assignments = null; if (!string.IsNullOrWhiteSpace(buyerId)) { try { assignments = await client.GetBuyerPriceListsAsync(buyerId); AppDebugLog.Info("Discount", $"Назначений прайсов покупателя: {assignments?.Count ?? 0}"); } catch (Exception ex) { AppDebugLog.Warning("Discount", $"Не удалось загрузить buyer price-lists: {ex.Message}"); } } List priceLists = null; try { priceLists = await client.GetPriceListsAsync(); AppDebugLog.Info("Discount", $"Прайс-листов: {priceLists?.Count ?? 0}"); } catch (Exception ex) { AppDebugLog.Warning("Discount", $"Не удалось загрузить price-lists: {ex.Message}"); } EnrichAssignmentsFromPriceLists(assignments, priceLists); if (assignments != null) { foreach (var a in assignments) { if (string.IsNullOrWhiteSpace(a?.PriceListId)) { continue; } var plId = a.PriceListId.Trim(); lookup.ByPriceListId[plId] = new ItemDiscountParts { PriceListId = plId, ClientDiscountPct = PositiveOrZero(a.MarkupPct), PriceListDiscountPct = PositiveOrZero(a.DefaultMarkupPct), RegionDiscountPct = 0m }; IndexSupplierKeys(lookup, a.SupplierId, a.SupplierName, plId); } } if (priceLists != null) { foreach (var pl in priceLists) { var plId = pl?.ResolvedId; if (string.IsNullOrWhiteSpace(plId)) { continue; } if (!lookup.ByPriceListId.TryGetValue(plId, out var parts)) { parts = new ItemDiscountParts { PriceListId = plId }; lookup.ByPriceListId[plId] = parts; } if (parts.PriceListDiscountPct <= 0m && pl.DefaultMarkupPct.HasValue && pl.DefaultMarkupPct.Value > 0m) { parts.PriceListDiscountPct = pl.DefaultMarkupPct.Value; } IndexSupplierKeys(lookup, pl.SupplierId, pl.SupplierName, plId); } } foreach (var plId in lookup.ByPriceListId.Keys.ToList()) { try { var regions = await client.GetPriceListRegionsAsync(plId); var regionPct = ResolveRegionDiscount(regions, regionId); lookup.ByPriceListId[plId].RegionDiscountPct = regionPct; } catch (Exception ex) { AppDebugLog.Warning("Discount", $"Регионы для прайса {plId}: {ex.Message}"); } } AppDebugLog.Info( "Discount", $"Готово: прайсов={lookup.ByPriceListId.Count}, привязок по поставщику={lookup.BySupplierKey.Count}, region={regionId}"); return lookup; } public static ItemDiscountParts ResolveForItem(DiscountLookup lookup, PriceSummaryItem item) { if (lookup == null || item == null) { return new ItemDiscountParts(); } var priceListId = (item.PriceListId ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(priceListId) && lookup.ByPriceListId.TryGetValue(priceListId, out var byId)) { return Clone(byId); } var supplierId = (item.SupplierId ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(supplierId) && lookup.BySupplierKey.TryGetValue(NormalizeKey(supplierId), out var plFromSupplier) && lookup.ByPriceListId.TryGetValue(plFromSupplier, out var bySupplier)) { return Clone(bySupplier); } var supplierName = (item.SupplierName ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(supplierName) && lookup.BySupplierKey.TryGetValue(NormalizeKey(supplierName), out var plFromName) && lookup.ByPriceListId.TryGetValue(plFromName, out var byName)) { return Clone(byName); } return new ItemDiscountParts(); } private static string ResolveBuyerId(List buyers, string loginHint) { if (buyers == null || buyers.Count == 0) { return null; } var login = (loginHint ?? string.Empty).Trim(); if (!string.IsNullOrWhiteSpace(login)) { var matched = buyers.FirstOrDefault(b => string.Equals((b.Username ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase) || string.Equals((b.Login ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase) || string.Equals((b.Name ?? string.Empty).Trim(), login, StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrWhiteSpace(matched?.ResolvedId)) { return matched.ResolvedId; } } if (buyers.Count == 1 && !string.IsNullOrWhiteSpace(buyers[0].ResolvedId)) { return buyers[0].ResolvedId; } return buyers .Select(b => b.ResolvedId) .FirstOrDefault(id => !string.IsNullOrWhiteSpace(id)); } private static void EnrichAssignmentsFromPriceLists( List assignments, List priceLists) { if (assignments == null || priceLists == null || priceLists.Count == 0) { return; } var byId = priceLists .Where(p => !string.IsNullOrWhiteSpace(p?.ResolvedId)) .GroupBy(p => p.ResolvedId, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase); foreach (var a in assignments) { if (a == null || string.IsNullOrWhiteSpace(a.PriceListId)) { continue; } if (!byId.TryGetValue(a.PriceListId.Trim(), out var pl)) { continue; } if (string.IsNullOrWhiteSpace(a.SupplierId)) { a.SupplierId = pl.SupplierId; } if (string.IsNullOrWhiteSpace(a.SupplierName)) { a.SupplierName = pl.SupplierName; } if ((!a.DefaultMarkupPct.HasValue || a.DefaultMarkupPct.Value <= 0m) && pl.DefaultMarkupPct.HasValue) { a.DefaultMarkupPct = pl.DefaultMarkupPct; } } } private static decimal ResolveRegionDiscount(List regions, string regionId) { if (regions == null || regions.Count == 0) { return 0m; } PriceListRegionDto match = null; if (!string.IsNullOrWhiteSpace(regionId)) { match = regions.FirstOrDefault(r => string.Equals((r.RegionId ?? string.Empty).Trim(), regionId, StringComparison.OrdinalIgnoreCase) && (r.InWork == null || r.InWork.Value)); } if (match == null) { match = regions.FirstOrDefault(r => r.InWork == true) ?? regions.FirstOrDefault(); } return PositiveOrZero(match?.MarkupPct); } private static void IndexSupplierKeys(DiscountLookup lookup, string supplierId, string supplierName, string priceListId) { if (lookup == null || string.IsNullOrWhiteSpace(priceListId)) { return; } if (!string.IsNullOrWhiteSpace(supplierId)) { lookup.BySupplierKey[NormalizeKey(supplierId)] = priceListId; } if (!string.IsNullOrWhiteSpace(supplierName)) { lookup.BySupplierKey[NormalizeKey(supplierName)] = priceListId; } } private static ItemDiscountParts Clone(ItemDiscountParts source) { if (source == null) { return new ItemDiscountParts(); } return new ItemDiscountParts { PriceListId = source.PriceListId, PriceListDiscountPct = source.PriceListDiscountPct, RegionDiscountPct = source.RegionDiscountPct, ClientDiscountPct = source.ClientDiscountPct }; } private static decimal PositiveOrZero(decimal? value) { return value.HasValue && value.Value > 0m ? value.Value : 0m; } private static string NormalizeKey(string value) { return (value ?? string.Empty).Trim().ToLowerInvariant(); } } public sealed class DiscountLookup { public string BuyerId { get; set; } public string RegionId { get; set; } public Dictionary ByPriceListId { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); public Dictionary BySupplierKey { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); } }