food-market/src/food-market.pos/Views/MainWindow.axaml
nns 0fd04d4f5c
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(pos): предупреждения о минусе на кассе — в строке + индикатор в шапке (офлайн)
Сервер уже слал остатки в sync (StockSyncDto), но касса их отбрасывала. Теперь:
- PosStore хранит снимок остатков (таблица stock, перезапись каждый синк — сервер
  шлёт полный снимок ненулевых); GetStock/NegativeCount/NegativeStocks.
- В строке чека при количестве больше остатка — «на остатке N шт — уйдёт в минус»
  (жёлтое, НЕ блокирует продажу — решение владельца). Работает офлайн по снимку.
- В шапке — постоянный жёлтый индикатор «N в минусе — нужна инвентаризация» по
  складу кассы, по тапу — список товаров (Flyout). Обновляется при старте, после
  чека и после синка.
- Тесты PosStore: снимок/перезапись, GetStock, NegativeCount/список (4/4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-09 03:38:50 +05:00

102 lines
8 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:foodmarket.Pos.ViewModels"
x:Class="foodmarket.Pos.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Width="1366" Height="768" MinWidth="1100" MinHeight="680"
Background="{DynamicResource PosBg}" Title="ALL MARKET · Касса"
WindowStartupLocation="CenterScreen">
<Panel>
<Grid RowDefinitions="64,*,40">
<!-- Верхняя полоса -->
<Border Grid.Row="0" Background="{DynamicResource PosCard}" BorderBrush="{DynamicResource PosBorder}" BorderThickness="0,0,0,1" Padding="24,0">
<Grid ColumnDefinitions="Auto,Auto,*,Auto" VerticalAlignment="Center">
<!-- Лого ALL MARKET (готовый файл; color на светлой теме, white на тёмной).
Высота 48 → ширина ≈ 140 px (≥ минимума брендбука), охранное поле — Spacing. -->
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="16" VerticalAlignment="Center">
<Image x:Name="BrandLogo" Height="48" VerticalAlignment="Center"/>
<Border Width="1" Height="26" Background="{DynamicResource PosBorder}" VerticalAlignment="Center"/>
<TextBlock Text="Касса" FontSize="17" Foreground="{DynamicResource PosTextMuted}" FontWeight="SemiBold" VerticalAlignment="Center"/>
</StackPanel>
<!-- Мета (сворачивается по ширине) -->
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="24" VerticalAlignment="Center" Margin="24,0,0,0">
<StackPanel x:Name="StoreMeta" Orientation="Horizontal" Spacing="24" VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center" FontSize="15" Foreground="{DynamicResource PosTextMuted}">
<Run Text="{Binding StoreName}" Foreground="{DynamicResource PosText}" FontWeight="SemiBold"/><Run Text=" · Точка "/><Run Text="{Binding PointName}" Foreground="{DynamicResource PosText}" FontWeight="SemiBold"/>
</TextBlock>
<Border Width="1" Height="28" Background="{DynamicResource PosBorder}"/>
</StackPanel>
<TextBlock VerticalAlignment="Center" FontSize="15" Foreground="{DynamicResource PosTextMuted}">
<Run Text="Кассир "/><Run Text="{Binding CashierName}" Foreground="{DynamicResource PosText}" FontWeight="SemiBold"/>
</TextBlock>
</StackPanel>
<!-- Сообщение об обновлении (в растяжимой зоне) -->
<TextBlock Grid.Column="2" VerticalAlignment="Center" Margin="24,0" FontSize="13" Foreground="{DynamicResource PosWarn}" Text="{Binding UpdateMessage}"
TextTrimming="CharacterEllipsis" IsVisible="{Binding UpdateMessage, Converter={x:Static ObjectConverters.IsNotNull}}"/>
<!-- Чипы статуса (всегда, никогда не обрезаются) -->
<StackPanel Grid.Column="3" Orientation="Horizontal" Spacing="10" VerticalAlignment="Center">
<!-- Постоянный жёлтый индикатор «N товаров в минусе» (по складу кассы).
По нажатию — список во всплывашке. Виден только при наличии минусов. -->
<Button Padding="12,6" CornerRadius="999" BorderThickness="0"
Background="#F59E0B" Foreground="#1F2937"
IsVisible="{Binding HasNegatives}" Command="{Binding ToggleNegativeListCommand}"
ToolTip.Tip="Товары в минусе — нужна инвентаризация">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="⚠" VerticalAlignment="Center"/>
<TextBlock Text="{Binding NegativeBadge}" VerticalAlignment="Center" FontWeight="SemiBold"/>
</StackPanel>
<Button.Flyout>
<Flyout Placement="Bottom">
<ScrollViewer MaxHeight="420" MaxWidth="380">
<TextBlock Text="{Binding NegativeListText}" TextWrapping="Wrap" FontSize="13"/>
</ScrollViewer>
</Flyout>
</Button.Flyout>
</Button>
<Border Classes="chip" Classes.ok="{Binding IsShiftOpen}" Classes.warn="{Binding !IsShiftOpen}">
<StackPanel Orientation="Horizontal" Spacing="8">
<Ellipse Width="8" Height="8" VerticalAlignment="Center" Fill="{DynamicResource PosBrand}"/>
<TextBlock Text="{Binding ShiftBadge}"/>
</StackPanel>
</Border>
<Border Classes="chip net">
<StackPanel Orientation="Horizontal" Spacing="8">
<Ellipse Width="8" Height="8" VerticalAlignment="Center" Fill="{DynamicResource PosBrand}"/>
<TextBlock Text="{Binding NetBadge}"/>
<TextBlock Text="{Binding QueueBadge}" IsVisible="{Binding QueueBadge, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
</StackPanel>
</Border>
</StackPanel>
</Grid>
</Border>
<!-- Текущая страница -->
<ContentControl Grid.Row="1" Content="{Binding CurrentPage}"/>
<!-- Полоса горячих клавиш -->
<Border Grid.Row="2" Background="{DynamicResource PosCard}" BorderBrush="{DynamicResource PosBorder}" BorderThickness="0,1,0,0" Padding="24,0">
<StackPanel Orientation="Horizontal" Spacing="28" VerticalAlignment="Center" HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F1"/></Border><TextBlock Text="Помощь" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F2"/></Border><TextBlock Text="Поиск" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F4"/></Border><TextBlock Text="Скидка" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F7"/></Border><TextBlock Text="Отложить" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F9"/></Border><TextBlock Text="Возврат" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F10"/></Border><TextBlock Text="Смена" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="F12"/></Border><TextBlock Text="Оплата" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="Del"/></Border><TextBlock Text="Удалить строку" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8"><Border Classes="kbd"><TextBlock Text="Esc"/></Border><TextBlock Text="Отмена" FontSize="14" Foreground="{DynamicResource PosTextMuted}" VerticalAlignment="Center"/></StackPanel>
</StackPanel>
</Border>
</Grid>
<!-- Модальная оплата -->
<Border Background="{DynamicResource PosScrim}" IsVisible="{Binding IsPaymentOpen}">
<ContentControl Content="{Binding Payment}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Panel>
</Window>