diff --git a/src/Elfisa.Avalonia/Styles/Elfisa.axaml b/src/Elfisa.Avalonia/Styles/Elfisa.axaml
index a56e3e1..70529ab 100644
--- a/src/Elfisa.Avalonia/Styles/Elfisa.axaml
+++ b/src/Elfisa.Avalonia/Styles/Elfisa.axaml
@@ -123,8 +123,8 @@
diff --git a/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs b/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs
new file mode 100644
index 0000000..b8fd1e5
--- /dev/null
+++ b/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Elfisa.Core;
+
+namespace Elfisa.Avalonia.ViewModels;
+
+/// «Загрузить» — скачивание актуального прайса с сервера в память.
+public partial class LoadPriceViewModel : ViewModelBase
+{
+ private readonly ApiClient _api = new();
+
+ [ObservableProperty] private bool _busy;
+ [ObservableProperty] private double _progress; // 0..100
+ [ObservableProperty] private string _progressText = "";
+ [ObservableProperty] private string? _result;
+
+ public string LastLoaded =>
+ PriceCache.LoadedAt is { } dt
+ ? $"Последняя загрузка: {dt:dd.MM.yyyy HH:mm} — {PriceCache.Items.Count} позиций"
+ : "Прайс ещё не загружался в этой сессии.";
+
+ public LoadPriceViewModel()
+ {
+ _api.SetToken(Session.Current.Token);
+ }
+
+ [RelayCommand]
+ private async Task LoadAsync()
+ {
+ Result = null;
+ try
+ {
+ Busy = true;
+ Progress = 0;
+ ProgressText = "Соединение с сервером…";
+
+ var all = new List();
+ const int page = 500;
+ int offset = 0, total = 0;
+
+ while (true)
+ {
+ var resp = await _api.GetPriceSummaryAsync(null, limit: page, offset: offset);
+ var batch = resp.Summary ?? new();
+ if (resp.TotalDrugs > 0) total = resp.TotalDrugs;
+ all.AddRange(batch);
+
+ Progress = total > 0 ? Math.Min(100.0, all.Count * 100.0 / total) : 0;
+ ProgressText = total > 0
+ ? $"Загружено {all.Count} из {total} позиций…"
+ : $"Загружено {all.Count} позиций…";
+
+ offset += batch.Count;
+ if (batch.Count < page || (total > 0 && all.Count >= total) || offset > 30000)
+ break;
+ }
+
+ PriceCache.Items = all;
+ PriceCache.LoadedAt = DateTime.Now;
+ Progress = 100;
+ ProgressText = "Готово.";
+
+ var suppliers = all.Select(x => x.SupplierName).Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().Count();
+ Result = $"Прайс загружен: {all.Count} позиций от {suppliers} поставщик(ов).";
+ OnPropertyChanged(nameof(LastLoaded));
+ }
+ catch (Exception ex)
+ {
+ Result = "Ошибка загрузки: " + ex.Message;
+ ProgressText = "";
+ }
+ finally { Busy = false; }
+ }
+}
diff --git a/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs b/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs
index 24b8851..060acba 100644
--- a/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs
+++ b/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs
@@ -46,6 +46,7 @@ public partial class ShellViewModel : ViewModelBase
new("Заказы", "🛒", () => new OrdersViewModel()),
new("Накладные", "📄", () => new InvoicesViewModel()),
new("Отчёты", "📊", () => new ReportsViewModel()),
+ new("Загрузить", "📥", () => new LoadPriceViewModel()),
new("Отправить", "📤", () => new SendOrderViewModel()),
new("Отказы", "⛔", () => new PlaceholderPageViewModel("Отказы",
"Отказные позиции по заказам. Нужен серверный эндпоинт: данные (RefuseItemsCount) сейчас есть только в локальной БД WinForms-клиента.")),
diff --git a/src/Elfisa.Avalonia/Views/LoadPriceView.axaml b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml
new file mode 100644
index 0000000..77737a0
--- /dev/null
+++ b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Elfisa.Avalonia/Views/LoadPriceView.axaml.cs b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml.cs
new file mode 100644
index 0000000..6669c87
--- /dev/null
+++ b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml.cs
@@ -0,0 +1,9 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Elfisa.Avalonia.Views;
+
+public partial class LoadPriceView : UserControl
+{
+ public LoadPriceView() => AvaloniaXamlLoader.Load(this);
+}
diff --git a/src/Elfisa.Avalonia/Views/ShellView.axaml b/src/Elfisa.Avalonia/Views/ShellView.axaml
index 5c4a0bb..fd7cf34 100644
--- a/src/Elfisa.Avalonia/Views/ShellView.axaml
+++ b/src/Elfisa.Avalonia/Views/ShellView.axaml
@@ -11,9 +11,9 @@
-
-
-
+
+
+
@@ -26,10 +26,10 @@
-
-
+
+
@@ -37,7 +37,7 @@
-
+
diff --git a/src/Elfisa.Core/PriceCache.cs b/src/Elfisa.Core/PriceCache.cs
new file mode 100644
index 0000000..9406157
--- /dev/null
+++ b/src/Elfisa.Core/PriceCache.cs
@@ -0,0 +1,9 @@
+namespace Elfisa.Core;
+
+/// Загруженный прайс в памяти (наполняется разделом «Загрузить»).
+public static class PriceCache
+{
+ public static List Items { get; set; } = new();
+ public static DateTime? LoadedAt { get; set; }
+ public static bool HasData => Items.Count > 0;
+}