diff --git a/src/Elfisa.Avalonia/ViewModels/SendOrderViewModel.cs b/src/Elfisa.Avalonia/ViewModels/SendOrderViewModel.cs
index bae2be9..4493bfd 100644
--- a/src/Elfisa.Avalonia/ViewModels/SendOrderViewModel.cs
+++ b/src/Elfisa.Avalonia/ViewModels/SendOrderViewModel.cs
@@ -9,98 +9,119 @@ using Elfisa.Core;
namespace Elfisa.Avalonia.ViewModels;
///
-/// Отправить: поиск по прайсу → корзина (общая с Прайс-листом) → создание заказа.
-/// ВНИМАНИЕ: отправка создаёт реальный заказ у поставщика.
+/// Отправить — отправка НЕОТПРАВЛЕННЫХ накладных (черновиков заказов из раздела
+/// «Заказы»). Список готовых накладных, кнопка «Отправить» (создаёт реальный заказ
+/// у поставщика). Без поиска по прайсу и наименований — только сами накладные.
///
public partial class SendOrderViewModel : ViewModelBase
{
private readonly ApiClient _api = new();
- private readonly CartService _cart = CartService.Instance;
- public ObservableCollection Results { get; } = new();
- public ObservableCollection Cart => _cart.Items;
- public ObservableCollection Locations { get; } = new();
+ /// Неотправленные накладные (черновики).
+ public ObservableCollection Drafts { get; } = new();
- [ObservableProperty] private string _search = "";
- [ObservableProperty] private PriceItem? _selectedResult;
- [ObservableProperty] private BuyerLocationDto? _selectedLocation;
- [ObservableProperty] private string _comment = "";
+ [ObservableProperty] private DraftOrder? _selected;
[ObservableProperty] private bool _busy;
[ObservableProperty] private string? _status;
- [ObservableProperty] private decimal _cartTotal;
+ [ObservableProperty] private decimal _totalSum;
+ [ObservableProperty] private bool _isEmpty = true;
+ [ObservableProperty] private int _count;
public SendOrderViewModel()
{
_api.SetToken(Session.Current.Token);
- _cart.Changed += Recalc;
- Recalc();
- if (Session.Current.IsAuthenticated) _ = InitAsync();
- }
-
- private async Task InitAsync()
- {
- try
- {
- var locs = await _api.GetBuyerLocationsAsync();
- Locations.Clear();
- foreach (var l in locs) Locations.Add(l);
- SelectedLocation = Locations.FirstOrDefault(l => l.IsDefault) ?? Locations.FirstOrDefault();
- await SearchAsync();
- }
- catch (Exception ex) { Status = ex.Message; }
+ Load();
}
[RelayCommand]
- private async Task SearchAsync()
+ private void Refresh() => Load();
+
+ private void Load()
{
- try
- {
- Busy = true; Status = null;
- var resp = await _api.GetPriceSummaryAsync(Search, limit: 200);
- Results.Clear();
- foreach (var it in resp.Summary ?? new()) Results.Add(it);
- }
- catch (Exception ex) { Status = ex.Message; }
- finally { Busy = false; }
+ Drafts.Clear();
+ foreach (var d in DraftStore.All.OrderByDescending(x => x.CreatedAt))
+ Drafts.Add(d);
+ TotalSum = Drafts.Sum(d => d.Total);
+ Count = Drafts.Count;
+ IsEmpty = Drafts.Count == 0;
+ if (Selected is null || !Drafts.Contains(Selected))
+ Selected = Drafts.FirstOrDefault();
}
+ /// Отправить одну накладную.
[RelayCommand]
- private void AddToCart()
- {
- if (SelectedResult is null) { Status = "Выберите позицию в списке слева."; return; }
- _cart.Add(SelectedResult);
- }
-
- [RelayCommand]
- private void RemoveFromCart(CartItem? item) => _cart.Remove(item!);
-
- private void Recalc() => CartTotal = _cart.Total;
-
- [RelayCommand]
- private async Task SendAsync()
+ private async Task SendAsync(DraftOrder? draft)
{
+ var d = draft ?? Selected;
+ if (d is null) { Status = "Выберите накладную для отправки."; return; }
Status = null;
- if (_cart.Count == 0) { Status = "Корзина пуста."; return; }
try
{
Busy = true;
- var req = new BuyerOrderCreateRequest
- {
- LocationId = SelectedLocation?.BuyerLocationId,
- Comment = string.IsNullOrWhiteSpace(Comment) ? null : Comment.Trim(),
- Items = _cart.Items.Select(c => new BuyerOrderItemReq
- {
- SupplierPriceId = c.Source.SupplierPriceId ?? "",
- Qty = (double)c.Qty,
- ItemName = c.Source.DrugName,
- Barcode = c.Source.Barcode
- }).ToList()
- };
- var resp = await _api.CreateOrderAsync(req);
- Status = $"✓ Заказ {resp.GlobalSign ?? resp.OrderId} создан: {resp.ItemsCount} поз., {resp.TotalAmount:N2}.";
- _cart.Clear();
+ var resp = await CreateFromDraft(d);
+ DraftStore.Remove(d.Id);
+ Status = $"✓ Накладная {resp.GlobalSign ?? resp.OrderId} отправлена ({resp.ItemsCount} поз., {resp.TotalAmount:N2}).";
}
catch (Exception ex) { Status = "Ошибка отправки: " + ex.Message; }
- finally { Busy = false; }
+ finally { Busy = false; Load(); }
+ }
+
+ /// Отправить все неотправленные накладные.
+ [RelayCommand]
+ private async Task SendAllAsync()
+ {
+ if (Drafts.Count == 0) { Status = "Нет неотправленных накладных."; return; }
+ Status = null;
+ int ok = 0;
+ string? lastErr = null;
+ try
+ {
+ Busy = true;
+ foreach (var d in DraftStore.All.ToList())
+ {
+ try
+ {
+ await CreateFromDraft(d);
+ DraftStore.Remove(d.Id);
+ ok++;
+ }
+ catch (Exception ex) { lastErr = ex.Message; }
+ }
+ }
+ finally { Busy = false; Load(); }
+
+ Status = lastErr is null
+ ? $"✓ Отправлено накладных: {ok}."
+ : $"Отправлено: {ok}. Не удалось часть — {lastErr}";
+ }
+
+ /// Удалить неотправленную накладную (не отправляя).
+ [RelayCommand]
+ private void Delete(DraftOrder? draft)
+ {
+ var d = draft ?? Selected;
+ if (d is null) return;
+ DraftStore.Remove(d.Id);
+ Load();
+ Status = "Накладная удалена.";
+ }
+
+ private async Task CreateFromDraft(DraftOrder d)
+ {
+ var req = new BuyerOrderCreateRequest
+ {
+ // Номер EX резервировался при сохранении черновика — используем его.
+ GlobalSign = d.Number.StartsWith("EX-", StringComparison.OrdinalIgnoreCase) ? d.Number : null,
+ LocationId = d.LocationId,
+ Comment = d.Comment,
+ Items = d.Items.Select(i => new BuyerOrderItemReq
+ {
+ SupplierPriceId = i.SupplierPriceId,
+ Qty = (double)i.Qty,
+ ItemName = i.Name,
+ Barcode = i.Barcode
+ }).ToList()
+ };
+ return await _api.CreateOrderAsync(req);
}
}
diff --git a/src/Elfisa.Avalonia/Views/SendOrderView.axaml b/src/Elfisa.Avalonia/Views/SendOrderView.axaml
index 3af0ce7..f8d1c8f 100644
--- a/src/Elfisa.Avalonia/Views/SendOrderView.axaml
+++ b/src/Elfisa.Avalonia/Views/SendOrderView.axaml
@@ -1,16 +1,19 @@
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+