������� �� Avalonia 2.0 (�����-���������, ����������, �������� ��������) #1
@ -52,14 +52,22 @@ dotnet publish src/Elfisa.Avalonia -c Release -r win-x64 --self-contained #
|
||||
|
||||
| Раздел | Что нужно | Сложность |
|
||||
|---|---|---|
|
||||
| Прайс-лист | поиск/каталог, наценки, добавление в заказ, большой грид | высокая |
|
||||
| Заказы | список + карточка заказа + позиции | средняя |
|
||||
| Накладные | список приходов | средняя |
|
||||
| Отправить | сборка заявки + выгрузка DBF (портировать `orderexport`) | средняя |
|
||||
| Отказы | список отказных позиций | низкая |
|
||||
| Справочник | контрагенты, грузополучатели, настройки | средняя |
|
||||
| Автообновление | аналог AutoUpdater под каждую ОС | средняя |
|
||||
| ✅ Прайс-лист | поиск + грид (/api/supplier-prices/summary) | готово (без добавления в заказ) |
|
||||
| ✅ Заказы | список + карточка заказа с позициями | готово |
|
||||
| ✅ Накладные | реестр приходных документов | готово |
|
||||
| ✅ Отчёты | 4 типа + ИИ → Word/Excel | готово |
|
||||
| ✅ Справочник | профиль + грузополучатели | готово |
|
||||
| ⏳ Отправить | сборка заявки (корзина) + выгрузка DBF (`orderexport`) | нужен flow создания заказа |
|
||||
| ⏳ Отказы | список отказных позиций | нужен эндпоинт |
|
||||
| ⏳ Автообновление | аналог AutoUpdater под каждую ОС | не начато |
|
||||
|
||||
**Порядок рекомендую:** Заказы → Прайс-лист → Отправить → остальное.
|
||||
Бизнес-логика каждого — обычный C#, переносится как есть; переписывается
|
||||
только UI (WinForms → Avalonia XAML), что и есть основная работа.
|
||||
**Прайс-лист/Заказы/Накладные** строятся на рабочих эндпоинтах
|
||||
(`supplier-prices/summary`, `buyer/report`). «Родные» `buyer/orders` и
|
||||
`buyer/catalog` возвращают 500 (баг квотинга PascalCase в GORM-билдере).
|
||||
Фикс для **buyer/orders** готов и застейджен (`es_api_service.exe.new`,
|
||||
apply-new-exe.bat) — после деплоя Заказы/Накладные перейдут на него.
|
||||
Каталог не используется (берём рабочий summary), его фикс — по желанию.
|
||||
|
||||
Осталось: **Отправить** (корзина + создание заказа + DBF) и **Отказы**,
|
||||
плюс автообновление и сохранение сессии. Логика переносится из WinForms,
|
||||
переписывается только UI (XAML).
|
||||
|
||||
48
src/Elfisa.Avalonia/ViewModels/DirectoryViewModel.cs
Normal file
48
src/Elfisa.Avalonia/ViewModels/DirectoryViewModel.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Elfisa.Core;
|
||||
|
||||
namespace Elfisa.Avalonia.ViewModels;
|
||||
|
||||
/// <summary>Справочник покупателя: профиль + грузополучатели (адреса).</summary>
|
||||
public partial class DirectoryViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClient _api = new();
|
||||
|
||||
public string UserName => Session.Current.Username ?? "";
|
||||
public string RoleLabel => Session.Current.Role switch
|
||||
{
|
||||
"buyer" => "Покупатель",
|
||||
"supplier" => "Поставщик",
|
||||
"manager" => "Менеджер",
|
||||
"admin" => "Администратор",
|
||||
_ => Session.Current.Role ?? ""
|
||||
};
|
||||
|
||||
public ObservableCollection<BuyerLocationDto> Locations { get; } = new();
|
||||
|
||||
[ObservableProperty] private bool _busy;
|
||||
[ObservableProperty] private string? _status;
|
||||
|
||||
public DirectoryViewModel()
|
||||
{
|
||||
_api.SetToken(Session.Current.Token);
|
||||
if (Session.Current.IsAuthenticated) _ = LoadAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Busy = true;
|
||||
var locs = await _api.GetBuyerLocationsAsync();
|
||||
Locations.Clear();
|
||||
foreach (var l in locs) Locations.Add(l);
|
||||
if (Locations.Count == 0) Status = "Грузополучатели не заданы.";
|
||||
}
|
||||
catch (Exception ex) { Status = ex.Message; }
|
||||
finally { Busy = false; }
|
||||
}
|
||||
}
|
||||
72
src/Elfisa.Avalonia/ViewModels/InvoicesViewModel.cs
Normal file
72
src/Elfisa.Avalonia/ViewModels/InvoicesViewModel.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Elfisa.Core;
|
||||
|
||||
namespace Elfisa.Avalonia.ViewModels;
|
||||
|
||||
public sealed class InvoiceRow
|
||||
{
|
||||
public string Date { get; init; } = "";
|
||||
public string Number { get; init; } = "";
|
||||
public string Supplier { get; init; } = "";
|
||||
public string Location { get; init; } = "";
|
||||
public int ItemsCount { get; init; }
|
||||
public decimal Sum { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Накладные — реестр приходных документов. Строится из /api/buyer/report
|
||||
/// (после деплоя фикса /api/buyer/orders можно перейти на «родной» источник).
|
||||
/// </summary>
|
||||
public partial class InvoicesViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClient _api = new();
|
||||
|
||||
public ObservableCollection<InvoiceRow> Rows { get; } = new();
|
||||
|
||||
[ObservableProperty] private bool _busy;
|
||||
[ObservableProperty] private string? _status;
|
||||
[ObservableProperty] private string _totals = "";
|
||||
|
||||
public InvoicesViewModel()
|
||||
{
|
||||
_api.SetToken(Session.Current.Token);
|
||||
if (Session.Current.IsAuthenticated) _ = LoadAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Busy = true;
|
||||
var to = DateTime.Today;
|
||||
var from = to.AddMonths(-24);
|
||||
var lines = await _api.GetBuyerReportAsync(from, to);
|
||||
|
||||
Rows.Clear();
|
||||
foreach (var g in lines.GroupBy(l => l.OrderId)
|
||||
.OrderByDescending(x => x.First().OrderDate ?? DateTime.MinValue))
|
||||
{
|
||||
var f = g.First();
|
||||
Rows.Add(new InvoiceRow
|
||||
{
|
||||
Date = f.OrderDate?.ToLocalTime().ToString("dd.MM.yyyy") ?? "",
|
||||
Number = f.GlobalSign ?? "",
|
||||
Supplier = string.Join(", ", g.Select(x => x.Supplier)
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct()),
|
||||
Location = f.Location ?? "",
|
||||
ItemsCount = g.Count(),
|
||||
Sum = g.Sum(x => x.Sum)
|
||||
});
|
||||
}
|
||||
|
||||
Totals = $"Накладных: {Rows.Count} Сумма: {Rows.Sum(r => r.Sum):N2}";
|
||||
if (Rows.Count == 0) Status = "Накладных за последние 2 года нет.";
|
||||
}
|
||||
catch (Exception ex) { Status = ex.Message; }
|
||||
finally { Busy = false; }
|
||||
}
|
||||
}
|
||||
45
src/Elfisa.Avalonia/ViewModels/PriceListViewModel.cs
Normal file
45
src/Elfisa.Avalonia/ViewModels/PriceListViewModel.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Elfisa.Core;
|
||||
|
||||
namespace Elfisa.Avalonia.ViewModels;
|
||||
|
||||
/// <summary>Прайс-лист (сводный прайс с /api/supplier-prices/summary — как в WinForms).</summary>
|
||||
public partial class PriceListViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClient _api = new();
|
||||
|
||||
public ObservableCollection<PriceItem> Items { get; } = new();
|
||||
|
||||
[ObservableProperty] private string _search = "";
|
||||
[ObservableProperty] private bool _busy;
|
||||
[ObservableProperty] private string? _status;
|
||||
[ObservableProperty] private int _total;
|
||||
|
||||
public PriceListViewModel()
|
||||
{
|
||||
_api.SetToken(Session.Current.Token);
|
||||
if (Session.Current.IsAuthenticated) _ = SearchAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SearchAsync()
|
||||
{
|
||||
Status = null;
|
||||
try
|
||||
{
|
||||
Busy = true;
|
||||
var resp = await _api.GetPriceSummaryAsync(Search, limit: 300);
|
||||
Items.Clear();
|
||||
foreach (var it in resp.Summary ?? new()) Items.Add(it);
|
||||
Total = resp.TotalDrugs;
|
||||
Status = $"Показано {Items.Count} из {Total} позиций"
|
||||
+ (string.IsNullOrWhiteSpace(Search) ? "" : $" по запросу «{Search.Trim()}»");
|
||||
}
|
||||
catch (Exception ex) { Status = ex.Message; }
|
||||
finally { Busy = false; }
|
||||
}
|
||||
}
|
||||
@ -42,13 +42,13 @@ public partial class ShellViewModel : ViewModelBase
|
||||
_logout = logout;
|
||||
NavItems = new ObservableCollection<NavItem>
|
||||
{
|
||||
new("Прайс-лист", "🧾", () => new PlaceholderPageViewModel("Прайс-лист", "Каталог, поиск и добавление позиций в заказ.")),
|
||||
new("Прайс-лист", "🧾", () => new PriceListViewModel()),
|
||||
new("Заказы", "🛒", () => new OrdersViewModel()),
|
||||
new("Накладные", "📄", () => new PlaceholderPageViewModel("Накладные", "Приход товара по заказам.")),
|
||||
new("Накладные", "📄", () => new InvoicesViewModel()),
|
||||
new("Отчёты", "📊", () => new ReportsViewModel()),
|
||||
new("Отправить", "📤", () => new PlaceholderPageViewModel("Отправить", "Формирование и выгрузка заявок поставщику (DBF).")),
|
||||
new("Отказы", "⛔", () => new PlaceholderPageViewModel("Отказы", "Отказные позиции по заказам.")),
|
||||
new("Справочник", "📖", () => new PlaceholderPageViewModel("Справочник", "Контрагенты, грузополучатели, настройки.")),
|
||||
new("Справочник", "📖", () => new DirectoryViewModel()),
|
||||
};
|
||||
SelectedNav = NavItems.First(n => n.Title == "Отчёты");
|
||||
}
|
||||
|
||||
56
src/Elfisa.Avalonia/Views/DirectoryView.axaml
Normal file
56
src/Elfisa.Avalonia/Views/DirectoryView.axaml
Normal file
@ -0,0 +1,56 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Elfisa.Avalonia.ViewModels"
|
||||
xmlns:core="using:Elfisa.Core"
|
||||
x:Class="Elfisa.Avalonia.Views.DirectoryView"
|
||||
x:DataType="vm:DirectoryViewModel">
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel Margin="24" Spacing="14">
|
||||
<StackPanel Spacing="2">
|
||||
<TextBlock Text="Справочник" Classes="h1"/>
|
||||
<TextBlock Text="Профиль и грузополучатели" Classes="muted"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Профиль -->
|
||||
<Border Classes="card" MaxWidth="720" HorizontalAlignment="Left">
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Text="Учётная запись" Classes="h2"/>
|
||||
<Grid ColumnDefinitions="160,*" RowDefinitions="Auto,Auto">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Логин" Classes="muted"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding UserName}" Foreground="{DynamicResource Text}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Роль" Classes="muted" Margin="0,6,0,0"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding RoleLabel}" Foreground="{DynamicResource Text}" Margin="0,6,0,0"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Грузополучатели -->
|
||||
<Border Classes="card" MaxWidth="720" HorizontalAlignment="Left">
|
||||
<StackPanel Spacing="10">
|
||||
<TextBlock Text="Грузополучатели (адреса)" Classes="h2"/>
|
||||
<ItemsControl ItemsSource="{Binding Locations}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="core:BuyerLocationDto">
|
||||
<Border Padding="12" Margin="0,0,0,8" CornerRadius="9"
|
||||
Background="{DynamicResource RowAlt}" BorderThickness="1" BorderBrush="{DynamicResource Border}">
|
||||
<StackPanel Spacing="3">
|
||||
<TextBlock Text="{Binding Address}" Foreground="{DynamicResource Text}" TextWrapping="Wrap"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBlock Text="{Binding RegionName}" Classes="muted"/>
|
||||
<Border Background="{DynamicResource AccentSoft}" CornerRadius="5" Padding="6,1"
|
||||
IsVisible="{Binding IsDefault}">
|
||||
<TextBlock Text="по умолчанию" FontSize="11" Foreground="{DynamicResource AccentPressed}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock Text="{Binding Status}" Classes="muted"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</UserControl>
|
||||
9
src/Elfisa.Avalonia/Views/DirectoryView.axaml.cs
Normal file
9
src/Elfisa.Avalonia/Views/DirectoryView.axaml.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Elfisa.Avalonia.Views;
|
||||
|
||||
public partial class DirectoryView : UserControl
|
||||
{
|
||||
public DirectoryView() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
39
src/Elfisa.Avalonia/Views/InvoicesView.axaml
Normal file
39
src/Elfisa.Avalonia/Views/InvoicesView.axaml
Normal file
@ -0,0 +1,39 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Elfisa.Avalonia.ViewModels"
|
||||
xmlns:conv="using:Avalonia.Data.Converters"
|
||||
x:Class="Elfisa.Avalonia.Views.InvoicesView"
|
||||
x:DataType="vm:InvoicesViewModel">
|
||||
|
||||
<DockPanel Margin="24">
|
||||
<Grid DockPanel.Dock="Top" Margin="0,0,0,14">
|
||||
<StackPanel Spacing="2" VerticalAlignment="Center">
|
||||
<TextBlock Text="Накладные" Classes="h1"/>
|
||||
<TextBlock Text="Реестр приходных документов" Classes="muted"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding Totals}" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
FontWeight="SemiBold" Foreground="{DynamicResource Accent}"/>
|
||||
</Grid>
|
||||
|
||||
<Border DockPanel.Dock="Top" Margin="0,0,0,10" Padding="10,7" CornerRadius="6"
|
||||
Background="{DynamicResource AccentSoft}"
|
||||
IsVisible="{Binding Status, Converter={x:Static conv:ObjectConverters.IsNotNull}}">
|
||||
<TextBlock Text="{Binding Status}" Foreground="{DynamicResource AccentPressed}" FontSize="12.5"/>
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" Padding="0" ClipToBounds="True">
|
||||
<DataGrid ItemsSource="{Binding Rows}" x:CompileBindings="False"
|
||||
AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Дата" Width="112" Binding="{Binding Date}"/>
|
||||
<DataGridTextColumn Header="Номер" Width="150" Binding="{Binding Number}"/>
|
||||
<DataGridTextColumn Header="Поставщик" Width="160" Binding="{Binding Supplier}"/>
|
||||
<DataGridTextColumn Header="Аптека" Width="*" Binding="{Binding Location}"/>
|
||||
<DataGridTextColumn Header="Позиций" Width="90" Binding="{Binding ItemsCount}" CellStyleClasses="num"/>
|
||||
<DataGridTextColumn Header="Сумма" Width="132" Binding="{Binding Sum, StringFormat='{}{0:N2}'}" CellStyleClasses="num"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
9
src/Elfisa.Avalonia/Views/InvoicesView.axaml.cs
Normal file
9
src/Elfisa.Avalonia/Views/InvoicesView.axaml.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Elfisa.Avalonia.Views;
|
||||
|
||||
public partial class InvoicesView : UserControl
|
||||
{
|
||||
public InvoicesView() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
44
src/Elfisa.Avalonia/Views/PriceListView.axaml
Normal file
44
src/Elfisa.Avalonia/Views/PriceListView.axaml
Normal file
@ -0,0 +1,44 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Elfisa.Avalonia.ViewModels"
|
||||
xmlns:conv="using:Avalonia.Data.Converters"
|
||||
x:Class="Elfisa.Avalonia.Views.PriceListView"
|
||||
x:DataType="vm:PriceListViewModel">
|
||||
|
||||
<DockPanel Margin="24">
|
||||
<Grid DockPanel.Dock="Top" Margin="0,0,0,14">
|
||||
<StackPanel Spacing="2" VerticalAlignment="Center">
|
||||
<TextBlock Text="Прайс-лист" Classes="h1"/>
|
||||
<TextBlock Text="Каталог доступных позиций и цены" Classes="muted"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border DockPanel.Dock="Top" Classes="card" Margin="0,0,0,12" Padding="16">
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBox Text="{Binding Search}" Width="440"
|
||||
Watermark="поиск по наименованию (от 2 символов)…"/>
|
||||
<Button Classes="primary" Content="Найти" Command="{Binding SearchCommand}" IsEnabled="{Binding !Busy}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border DockPanel.Dock="Top" Margin="0,0,0,10" Padding="10,7" CornerRadius="6"
|
||||
Background="{DynamicResource AccentSoft}"
|
||||
IsVisible="{Binding Status, Converter={x:Static conv:ObjectConverters.IsNotNull}}">
|
||||
<TextBlock Text="{Binding Status}" Foreground="{DynamicResource AccentPressed}" FontSize="12.5"/>
|
||||
</Border>
|
||||
|
||||
<Border Classes="card" Padding="0" ClipToBounds="True">
|
||||
<DataGrid ItemsSource="{Binding Items}" x:CompileBindings="False"
|
||||
AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Наименование" Width="*" Binding="{Binding DrugName}"/>
|
||||
<DataGridTextColumn Header="Форма" Width="150" Binding="{Binding CureForm}"/>
|
||||
<DataGridTextColumn Header="Производитель" Width="220" Binding="{Binding Manufacturer}"/>
|
||||
<DataGridTextColumn Header="Поставщик" Width="130" Binding="{Binding SupplierName}"/>
|
||||
<DataGridTextColumn Header="Цена" Width="110" Binding="{Binding Price, StringFormat='{}{0:N2}'}" CellStyleClasses="num"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
9
src/Elfisa.Avalonia/Views/PriceListView.axaml.cs
Normal file
9
src/Elfisa.Avalonia/Views/PriceListView.axaml.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Elfisa.Avalonia.Views;
|
||||
|
||||
public partial class PriceListView : UserControl
|
||||
{
|
||||
public PriceListView() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
@ -97,6 +97,26 @@ public sealed class ApiClient
|
||||
return new AiReportResult { Content = bytes, FileName = name.Trim('"') };
|
||||
}
|
||||
|
||||
/// <summary>Сводный прайс (то же, что грузит WinForms-десктоп). Поиск по q (от 2 символов).</summary>
|
||||
public async Task<PriceSummaryResponse> GetPriceSummaryAsync(string? q = null, int limit = 200, int offset = 0)
|
||||
{
|
||||
EnsureAuth();
|
||||
var path = $"/api/supplier-prices/summary?limit={limit}&offset={offset}";
|
||||
if (!string.IsNullOrWhiteSpace(q) && q.Trim().Length >= 2)
|
||||
path += "&q=" + Uri.EscapeDataString(q.Trim());
|
||||
var text = await AuthorizedGetAsync(path, "загрузку прайса").ConfigureAwait(false);
|
||||
return JsonSerializer.Deserialize<PriceSummaryResponse>(text, Json) ?? new PriceSummaryResponse();
|
||||
}
|
||||
|
||||
/// <summary>Адреса-грузополучатели покупателя.</summary>
|
||||
public async Task<List<BuyerLocationDto>> GetBuyerLocationsAsync()
|
||||
{
|
||||
EnsureAuth();
|
||||
var text = await AuthorizedGetAsync("/api/buyer/locations", "загрузку грузополучателей").ConfigureAwait(false);
|
||||
try { return JsonSerializer.Deserialize<List<BuyerLocationDto>>(text, Json) ?? new(); }
|
||||
catch { return new(); }
|
||||
}
|
||||
|
||||
private async Task<string> AuthorizedGetAsync(string path, string op)
|
||||
{
|
||||
using var req = new HttpRequestMessage(HttpMethod.Get, Url(path));
|
||||
|
||||
@ -52,3 +52,31 @@ public sealed class ErrorResponse
|
||||
{
|
||||
[JsonPropertyName("error")] public string? Error { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PriceSummaryResponse
|
||||
{
|
||||
[JsonPropertyName("summary")] public List<PriceItem>? Summary { get; set; }
|
||||
[JsonPropertyName("total_drugs")] public int TotalDrugs { get; set; }
|
||||
}
|
||||
|
||||
public sealed class PriceItem
|
||||
{
|
||||
[JsonPropertyName("supplier_price_id")] public string? SupplierPriceId { get; set; }
|
||||
[JsonPropertyName("supplier_name")] public string? SupplierName { get; set; }
|
||||
[JsonPropertyName("drug_name")] public string? DrugName { get; set; }
|
||||
[JsonPropertyName("cure_form")] public string? CureForm { get; set; }
|
||||
[JsonPropertyName("barcode")] public string? Barcode { get; set; }
|
||||
[JsonPropertyName("price")] public decimal Price { get; set; }
|
||||
[JsonPropertyName("quantity")] public decimal Quantity { get; set; }
|
||||
[JsonPropertyName("manufacturer")] public string? Manufacturer { get; set; }
|
||||
[JsonPropertyName("trade_name")] public string? TradeName { get; set; }
|
||||
[JsonPropertyName("es_code")] public long EsCode { get; set; }
|
||||
}
|
||||
|
||||
public sealed class BuyerLocationDto
|
||||
{
|
||||
[JsonPropertyName("buyer_location_id")] public string? BuyerLocationId { get; set; }
|
||||
[JsonPropertyName("address")] public string? Address { get; set; }
|
||||
[JsonPropertyName("region_name")] public string? RegionName { get; set; }
|
||||
[JsonPropertyName("is_default")] public bool IsDefault { get; set; }
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user