Добавьте файлы проекта.
3
ChudoObuv.slnx
Normal file
@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="ChudoObuv/ChudoObuv.csproj" />
|
||||
</Solution>
|
||||
57
ChudoObuv/App.xaml
Normal file
@ -0,0 +1,57 @@
|
||||
<Application x:Class="ChudoObuv.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml"
|
||||
DispatcherUnhandledException="OnDispatcherUnhandledException">
|
||||
<Application.Resources>
|
||||
<!-- Цвета из руководства по стилю -->
|
||||
<SolidColorBrush x:Key="MainBackgroundBrush" Color="#FFFFFF" />
|
||||
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#D2F6E7" />
|
||||
<SolidColorBrush x:Key="AccentBrush" Color="#70B2AF" />
|
||||
<SolidColorBrush x:Key="LowStockBrush" Color="#FF8080" />
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="FontFamily" Value="Calibri" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="Background" Value="{StaticResource SecondaryBackgroundBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource AccentBrush}" />
|
||||
<Setter Property="Padding" Value="12,4" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
<Setter Property="MinHeight" Value="32" />
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
</Style>
|
||||
<!-- Кнопка целевого действия (акцентный цвет) -->
|
||||
<Style x:Key="AccentButton" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
|
||||
<Setter Property="Background" Value="{StaticResource AccentBrush}" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="FontFamily" Value="Calibri" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="Padding" Value="4,2" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinHeight" Value="30" />
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="FontFamily" Value="Calibri" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
<Setter Property="MinHeight" Value="30" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
</Style>
|
||||
<Style TargetType="DataGrid">
|
||||
<Setter Property="FontFamily" Value="Calibri" />
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="AutoGenerateColumns" Value="False" />
|
||||
<Setter Property="IsReadOnly" Value="True" />
|
||||
<Setter Property="CanUserAddRows" Value="False" />
|
||||
<Setter Property="SelectionMode" Value="Single" />
|
||||
<Setter Property="HeadersVisibility" Value="Column" />
|
||||
<Setter Property="AlternatingRowBackground" Value="{StaticResource
|
||||
SecondaryBackgroundBrush}" />
|
||||
<Setter Property="Margin" Value="4" />
|
||||
</Style>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
32
ChudoObuv/App.xaml.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
using System.Globalization;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Threading;
|
||||
using ChudoObuv.Logic;
|
||||
|
||||
namespace ChudoObuv
|
||||
{
|
||||
public partial class App : Application
|
||||
{
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// Русский формат чисел, цен и дат во всем приложении (в том числе в привязках XAML)
|
||||
CultureInfo russian = new CultureInfo("ru-RU");
|
||||
CultureInfo.DefaultThreadCurrentCulture = russian;
|
||||
CultureInfo.DefaultThreadCurrentUICulture = russian;
|
||||
Thread.CurrentThread.CurrentCulture = russian;
|
||||
Thread.CurrentThread.CurrentUICulture = russian;
|
||||
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement),
|
||||
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(russian.IetfLanguageTag)));
|
||||
base.OnStartup(e);
|
||||
}
|
||||
// Последняя защита от аварийного завершения: любая необработанная ошибка показывается пользователю
|
||||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
Messages.ShowError($"Произошла непредвиденная ошибка: {e.Exception.Message}\nПовторите действие.Если ошибка повторяется, перезапустите приложение.");
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
ChudoObuv/AssemblyInfo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
24
ChudoObuv/ChudoObuv.csproj
Normal file
@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>Resources\app.ico</ApplicationIcon>
|
||||
<AssemblyTitle>Чудо Обувь</AssemblyTitle>
|
||||
<Product>Чудо Обувь</Product>
|
||||
<Company>Чудо Обувь</Company>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MySqlConnector" Version="2.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\app.ico" />
|
||||
<Resource Include="Resources\logo.png" />
|
||||
<Content Include="Resources\picture.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="Resources\Images\*.png" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
321
ChudoObuv/Data/DataService.cs
Normal file
@ -0,0 +1,321 @@
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
using MySqlConnector;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace ChudoObuv.Data
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Все запросы приложения к базе данных.
|
||||
/// </summary>
|
||||
public static class DataService
|
||||
{
|
||||
// ---------------------------- Пользователи ----------------------------
|
||||
public static User FindUserByLogin(string login)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT u.user_id, u.last_name, u.first_name, u.patronymic, u.login, r.role_name
|
||||
FROM users u
|
||||
JOIN roles r ON r.role_id = u.role_id
|
||||
WHERE u.login = @login";
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
command.Parameters.AddWithValue("@login", login);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
return reader.Read() ? ReadUser(reader) : null;
|
||||
}
|
||||
public static List<User> GetClients()
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT u.user_id, u.last_name, u.first_name, u.patronymic, u.login, r.role_name
|
||||
FROM users u
|
||||
JOIN roles r ON r.role_id = u.role_id
|
||||
WHERE r.role_name = @role
|
||||
ORDER BY u.last_name, u.first_name";
|
||||
List<User> clients = new List<User>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
command.Parameters.AddWithValue("@role", User.ClientRole);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
clients.Add(ReadUser(reader));
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
private static User ReadUser(MySqlDataReader reader)
|
||||
{
|
||||
return new User
|
||||
{
|
||||
Id = reader.GetInt32("user_id"),
|
||||
LastName = reader.GetString("last_name"),
|
||||
FirstName = reader.GetString("first_name"),
|
||||
Patronymic = reader.IsDBNull(reader.GetOrdinal("patronymic")) ? "" : reader.GetString("patronymic"),
|
||||
Login = reader.GetString("login"),
|
||||
Role = reader.GetString("role_name")
|
||||
};
|
||||
}
|
||||
// ------------------------------- Товары -------------------------------
|
||||
public static List<Product> GetProducts()
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT p.product_id, p.product_name, c.category_name, s.subcategory_name,m.manufacture_name,p.description, p.composition, p.price, p.image_file,
|
||||
|
||||
COALESCE((SELECT SUM(si.quantity) FROM stock_items si WHERE si.product_id = p.product_id), 0) AS total_quantity,
|
||||
EXISTS (SELECT 1 FROM order_items oi
|
||||
|
||||
JOIN orders o ON o.order_id = oi.order_id
|
||||
JOIN stock_items si ON si.stock_item_id = oi.stock_item_id
|
||||
WHERE si.product_id = p.product_id
|
||||
AND o.order_date >= @previousMonthStart
|
||||
|
||||
AND o.order_date < @currentMonthStart) AS has_previous_month_orders
|
||||
|
||||
FROM products p
|
||||
JOIN categories c ON c.category_id = p.category_id
|
||||
JOIN subcategories s ON s.subcategory_id = p.subcategory_id
|
||||
JOIN manufacturers m ON m.manufacture_id = p.manufacturer_id
|
||||
|
||||
ORDER BY p.product_id";
|
||||
DateTime today = DateTime.Today;
|
||||
List<Product> products = new List<Product>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
command.Parameters.AddWithValue("@previousMonthStart", PriceCalculator.GetPreviousMonthStart(today));
|
||||
command.Parameters.AddWithValue("@currentMonthStart", PriceCalculator.GetCurrentMonthStart(today));
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
products.Add(new Product
|
||||
{
|
||||
Id = reader.GetInt32("product_id"),
|
||||
Name = reader.GetString("product_name"),
|
||||
Category = reader.GetString("category_name"),
|
||||
Subcategory = reader.GetString("subcategory_name"),
|
||||
Manufacturer = reader.GetString("manufacture_name"),
|
||||
Description = reader.IsDBNull(reader.GetOrdinal("description")) ? "" :
|
||||
|
||||
reader.GetString("description"),
|
||||
Composition = reader.IsDBNull(reader.GetOrdinal("composition")) ? "" :
|
||||
reader.GetString("composition"),
|
||||
Price = reader.GetDecimal("price"),
|
||||
|
||||
ImageFile = reader.IsDBNull(reader.GetOrdinal("image_file")) ? null :
|
||||
|
||||
reader.GetString("image_file"),
|
||||
TotalQuantity = Convert.ToInt32(reader["total_quantity"]),
|
||||
|
||||
HasOrdersInPreviousMonth = Convert.ToInt32(reader["has_previous_month_orders"]) == 1
|
||||
|
||||
});
|
||||
}
|
||||
return products;
|
||||
}
|
||||
public static List<string> GetCategories()
|
||||
{
|
||||
List<string> categories = new List<string>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand("SELECT category_name FROM categories ORDER BY category_name", connection);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
categories.Add(reader.GetString(0));
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
/// <summary>
|
||||
/// Все товарные позиции модели (размерный ряд) с доступным количеством.
|
||||
/// </summary>
|
||||
public static List<StockItem> GetStockItems(int productId)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT si.stock_item_id, si.product_id, s.size_value, si.quantity
|
||||
FROM stock_items si
|
||||
JOIN sizes s ON s.size_id = si.size_id
|
||||
WHERE si.product_id = @productId
|
||||
ORDER BY s.size_value";
|
||||
List<StockItem> items = new List<StockItem>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
command.Parameters.AddWithValue("@productId", productId);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
items.Add(new StockItem
|
||||
{
|
||||
Id = reader.GetInt32("stock_item_id"),
|
||||
ProductId = reader.GetInt32("product_id"),
|
||||
Size = reader.GetDecimal("size_value"),
|
||||
Quantity = reader.GetInt32("quantity")
|
||||
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
// ------------------------------- Заказы -------------------------------
|
||||
|
||||
public static List<Order> GetOrders()
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT o.order_id, o.order_date, u.user_id,
|
||||
CONCAT_WS(' ', u.last_name, u.first_name, u.patronymic) AS client_name,
|
||||
|
||||
COUNT(oi.order_item_id) AS items_count,
|
||||
COALESCE(SUM(oi.quantity * oi.unit_price), 0) AS total_sum
|
||||
|
||||
FROM orders o
|
||||
JOIN users u ON u.user_id = o.user_id
|
||||
LEFT JOIN order_items oi ON oi.order_id = o.order_id
|
||||
GROUP BY o.order_id, o.order_date, u.user_id, client_name
|
||||
ORDER BY o.order_date DESC, o.order_id DESC";
|
||||
List<Order> orders = new List<Order>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
orders.Add(new Order
|
||||
{
|
||||
Id = reader.GetInt32("order_id"),
|
||||
|
||||
OrderDate = reader.GetDateTime("order_date"),
|
||||
ClientId = reader.GetInt32("user_id"),
|
||||
ClientFullName = reader.GetString("client_name"),
|
||||
ItemsCount = Convert.ToInt32(reader["items_count"]),
|
||||
TotalSum = reader.GetDecimal("total_sum")
|
||||
|
||||
});
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
public static List<OrderItem> GetOrderItems(int orderId)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT oi.order_item_id, oi.stock_item_id, p.product_name, m.manufacturer_name, s.size_value, oi.quantity, oi.unit_price
|
||||
FROM order_items oi
|
||||
JOIN stock_items si ON si.stock_item_id = oi.stock_item_id
|
||||
JOIN products p ON p.product_id = si.product_id
|
||||
JOIN manufacturers m ON m.manufacturer_id = p.manufacturer_id
|
||||
JOIN sizes s ON s.size_id = si.size_id
|
||||
WHERE oi.order_id = @orderId
|
||||
ORDER BY oi.order_item_id";
|
||||
List<OrderItem> items = new List<OrderItem>();
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand(sql, connection);
|
||||
command.Parameters.AddWithValue("@orderId", orderId);
|
||||
using MySqlDataReader reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
items.Add(new OrderItem
|
||||
{
|
||||
Id = reader.GetInt32("order_item_id"),
|
||||
StockItemId = reader.GetInt32("stock_item_id"),
|
||||
ProductName = reader.GetString("product_name"),
|
||||
Manufacturer = reader.GetString("manufacturer_name"),
|
||||
Size = reader.GetDecimal("size_value"),
|
||||
Quantity = reader.GetInt32("quantity"),
|
||||
UnitPrice = reader.GetDecimal("unit_price")
|
||||
|
||||
});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
/// <summary>
|
||||
/// Сохраняет заказ и уменьшает остатки. Возвращает номер заказа.
|
||||
/// Если какого-то размера уже не хватает — заказ не сохраняется (исключение).
|
||||
/// </summary>
|
||||
public static int CreateOrder(int clientId, List<OrderItem> items)
|
||||
{
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlTransaction transaction = connection.BeginTransaction();
|
||||
MySqlCommand orderCommand = new MySqlCommand(
|
||||
|
||||
"INSERT INTO orders (order_date, user_id) VALUES (@date, @userId)", connection,
|
||||
transaction);
|
||||
orderCommand.Parameters.AddWithValue("@date", DateTime.Today);
|
||||
orderCommand.Parameters.AddWithValue("@userId", clientId);
|
||||
orderCommand.ExecuteNonQuery();
|
||||
int orderId = (int)orderCommand.LastInsertedId;
|
||||
foreach (OrderItem item in items)
|
||||
{
|
||||
// Остаток уменьшается только если его хватает — защита от одновременных заказов
|
||||
MySqlCommand stockCommand = new MySqlCommand(
|
||||
"UPDATE stock_items SET quantity = quantity - @quantity WHERE stock_item_id = @id AND quantity >= @quantity",
|
||||
connection, transaction);
|
||||
stockCommand.Parameters.AddWithValue("@quantity", item.Quantity);
|
||||
stockCommand.Parameters.AddWithValue("@id", item.StockItemId);
|
||||
if (stockCommand.ExecuteNonQuery() == 0)
|
||||
{
|
||||
transaction.Rollback();
|
||||
|
||||
throw new InvalidOperationException(
|
||||
|
||||
$"Недостаточно товара «{item.ProductName}», размер {item.SizeText}. Обновите каталог и измените количество.");
|
||||
}
|
||||
MySqlCommand itemCommand = new MySqlCommand(
|
||||
"INSERT INTO order_items (order_id, stock_item_id, quantity, unit_price) VALUES (@orderId, @stockItemId, @quantity, @price)",
|
||||
connection, transaction);
|
||||
itemCommand.Parameters.AddWithValue("@orderId", orderId);
|
||||
itemCommand.Parameters.AddWithValue("@stockItemId", item.StockItemId);
|
||||
itemCommand.Parameters.AddWithValue("@quantity", item.Quantity);
|
||||
itemCommand.Parameters.AddWithValue("@price", item.UnitPrice);
|
||||
itemCommand.ExecuteNonQuery();
|
||||
}
|
||||
transaction.Commit();
|
||||
return orderId;
|
||||
}
|
||||
/// <summary>
|
||||
/// Удаляет заказ и возвращает его товары на склад.
|
||||
/// </summary>
|
||||
public static void DeleteOrder(int orderId)
|
||||
{
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlTransaction transaction = connection.BeginTransaction();
|
||||
MySqlCommand returnCommand = new MySqlCommand(@"
|
||||
UPDATE stock_items si
|
||||
JOIN order_items oi ON oi.stock_item_id = si.stock_item_id
|
||||
SET si.quantity = si.quantity + oi.quantity
|
||||
WHERE oi.order_id = @orderId", connection, transaction);
|
||||
returnCommand.Parameters.AddWithValue("@orderId", orderId);
|
||||
returnCommand.ExecuteNonQuery();
|
||||
// Строки состава удаляются каскадно (ON DELETE CASCADE)
|
||||
MySqlCommand deleteCommand = new MySqlCommand("DELETE FROM orders WHERE order_id = @orderId", connection, transaction);
|
||||
deleteCommand.Parameters.AddWithValue("@orderId", orderId);
|
||||
deleteCommand.ExecuteNonQuery();
|
||||
transaction.Commit();
|
||||
}
|
||||
/// <summary>
|
||||
/// Удаляет товарную позицию из заказа и возвращает ее количество на склад.
|
||||
/// </summary>
|
||||
public static void DeleteOrderItem(int orderItemId)
|
||||
{
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlTransaction transaction = connection.BeginTransaction();
|
||||
MySqlCommand returnCommand = new MySqlCommand(@"
|
||||
UPDATE stock_items si
|
||||
JOIN order_items oi ON oi.stock_item_id = si.stock_item_id
|
||||
SET si.quantity = si.quantity + oi.quantity
|
||||
WHERE oi.order_item_id = @id", connection, transaction);
|
||||
returnCommand.Parameters.AddWithValue("@id", orderItemId);
|
||||
returnCommand.ExecuteNonQuery();
|
||||
MySqlCommand deleteCommand = new MySqlCommand("DELETE FROM order_items WHERE order_item_id = @id", connection, transaction);
|
||||
deleteCommand.Parameters.AddWithValue("@id", orderItemId);
|
||||
deleteCommand.ExecuteNonQuery();
|
||||
transaction.Commit();
|
||||
}
|
||||
public static void UpdateOrderDate(int orderId, DateTime orderDate)
|
||||
{
|
||||
using MySqlConnection connection = Database.OpenConnection();
|
||||
using MySqlCommand command = new MySqlCommand("UPDATE orders SET order_date = @date WHERE order_id = @orderId", connection);
|
||||
command.Parameters.AddWithValue("@date", orderDate.Date);
|
||||
command.Parameters.AddWithValue("@orderId", orderId);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
ChudoObuv/Data/Database.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace ChudoObuv.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Подключение к базе данных MySQL.
|
||||
/// </summary>
|
||||
public static class Database
|
||||
{
|
||||
// Перед запуском укажите свои сервер, имя базы, пользователя и пароль MySQL
|
||||
private const string ConnectionString = "Server=177.1.181.207;Port=3306;Database=isp-41_lipatova_yuv_chudo_obuv;UserID=isp-41_lipatova_yuv;Password=isp-41_lipatova_yuv;CharSet=utf8mb4;";
|
||||
public static MySqlConnection OpenConnection()
|
||||
{
|
||||
MySqlConnection connection = new MySqlConnection(ConnectionString);
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
56
ChudoObuv/Logic/Messages.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace ChudoObuv.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Окна сообщений с заголовком и пиктограммой нужного типа.
|
||||
/// </summary>
|
||||
public static class Messages
|
||||
|
||||
|
||||
{
|
||||
private const string AppName = "Чудо Обувь";
|
||||
public static void ShowError(string text)
|
||||
{
|
||||
MessageBox.Show(text, $"{AppName} — Ошибка", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
public static void ShowWarning(string text)
|
||||
{
|
||||
MessageBox.Show(text, $"{AppName} — Предупреждение", MessageBoxButton.OK,
|
||||
MessageBoxImage.Warning);
|
||||
}
|
||||
public static void ShowInfo(string text)
|
||||
{
|
||||
MessageBox.Show(text, $"{AppName} — Информация", MessageBoxButton.OK,
|
||||
MessageBoxImage.Information);
|
||||
}
|
||||
/// <summary>
|
||||
/// Подтверждение необратимой операции. Возвращает true, если пользователь нажал «Да».
|
||||
/// </summary>
|
||||
public static bool Confirm(string text)
|
||||
{
|
||||
MessageBoxResult result = MessageBox.Show(text, $"{AppName} — Подтверждение",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
||||
return result == MessageBoxResult.Yes;
|
||||
}
|
||||
public static void ShowDatabaseError(Exception exception)
|
||||
{
|
||||
if (exception is MySqlException)
|
||||
{
|
||||
ShowError("Не удалось выполнить операцию с базой данных.\n" +
|
||||
$"Причина: {exception.Message}\n\n" +
|
||||
"Проверьте, что сервер MySQL запущен, а в файле Data\\Database.cs указаны верные сервер, пользователь и пароль.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowError(exception.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
ChudoObuv/Logic/PriceCalculator.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChudoObuv.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Алгоритмы расчета показателей товара.
|
||||
/// </summary>
|
||||
public static class PriceCalculator
|
||||
{
|
||||
public const decimal DiscountPercent = 25m;
|
||||
public const int ManyQuantityThreshold = 5;
|
||||
public const int LowStockThreshold = 3;
|
||||
/// <summary>
|
||||
/// Начало предыдущего календарного месяца относительно даты расчета.
|
||||
/// </summary>
|
||||
public static DateTime GetPreviousMonthStart(DateTime calculationDate)
|
||||
{
|
||||
return GetCurrentMonthStart(calculationDate).AddMonths(-1);
|
||||
}
|
||||
public static DateTime GetCurrentMonthStart(DateTime calculationDate)
|
||||
{
|
||||
return new DateTime(calculationDate.Year, calculationDate.Month, 1);
|
||||
}
|
||||
/// <summary>
|
||||
/// Цена со скидкой 25%, если в предыдущем календарном месяце заказов товара не было.
|
||||
/// </summary>
|
||||
public static decimal CalculateDiscountedPrice(decimal price, bool hasOrdersInPreviousMonth)
|
||||
{
|
||||
if (hasOrdersInPreviousMonth)
|
||||
{
|
||||
return price;
|
||||
}
|
||||
return Math.Round(price * (100m - DiscountPercent) / 100m, 2);
|
||||
}
|
||||
/// <summary>
|
||||
/// «много», если пар больше пяти, иначе «мало».
|
||||
/// </summary>
|
||||
public static string GetQuantityLabel(int totalQuantity)
|
||||
{
|
||||
return totalQuantity > ManyQuantityThreshold ? "много" : "мало";
|
||||
}
|
||||
public static bool IsLowStock(int totalQuantity)
|
||||
{
|
||||
return totalQuantity <= LowStockThreshold;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
ChudoObuv/Logic/Session.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ChudoObuv.Models;
|
||||
namespace ChudoObuv.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Текущий пользователь и формируемый (еще не подтвержденный) заказ.
|
||||
/// </summary>
|
||||
public static class Session
|
||||
{
|
||||
public static User CurrentUser { get; private set; }
|
||||
/// <summary>
|
||||
/// Клиент, для которого формируется заказ: сам пользователь или клиент, выбранный менеджером.
|
||||
/// </summary>
|
||||
public static User OrderClient { get; private set; }
|
||||
public static List<OrderItem> Cart { get; } = new List<OrderItem>();
|
||||
public static bool IsAuthorized => CurrentUser != null;
|
||||
public static void Login(User user)
|
||||
{
|
||||
CurrentUser = user;
|
||||
OrderClient = user;
|
||||
Cart.Clear();
|
||||
}
|
||||
public static void Logout()
|
||||
{
|
||||
CurrentUser = null;
|
||||
OrderClient = null;
|
||||
Cart.Clear();
|
||||
}
|
||||
public static void StartOrderFor(User client)
|
||||
{
|
||||
OrderClient = client;
|
||||
Cart.Clear();
|
||||
}
|
||||
public static void CancelOrder()
|
||||
{
|
||||
Cart.Clear();
|
||||
OrderClient = CurrentUser;
|
||||
}
|
||||
/// <summary>
|
||||
/// Сколько пар данной товарной позиции уже лежит в формируемом заказе.
|
||||
/// </summary>
|
||||
public static int GetQuantityInCart(int stockItemId)
|
||||
{
|
||||
return Cart.Where(item => item.StockItemId == stockItemId).Sum(item => item.Quantity);
|
||||
}
|
||||
public static void AddToCart(OrderItem newItem)
|
||||
{
|
||||
OrderItem existing = Cart.FirstOrDefault(item => item.StockItemId == newItem.StockItemId);
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Quantity += newItem.Quantity;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cart.Add(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
43
ChudoObuv/MainWindow.xaml
Normal file
@ -0,0 +1,43 @@
|
||||
<Window x:Class="ChudoObuv.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Чудо Обувь"
|
||||
Icon="Resources/app.ico"
|
||||
Width="1100" Height="750" MinWidth="900" MinHeight="600"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
FontFamily="Calibri" FontSize="16"
|
||||
Background="{StaticResource MainBackgroundBrush}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Шапка: логотип, заголовок страницы, ФИО пользователя -->
|
||||
<Grid Grid.Row="0" Background="{StaticResource SecondaryBackgroundBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Source="Resources/logo.png" Height="64" Margin="10,6"
|
||||
Stretch="Uniform" RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Button Grid.Column="1" x:Name="BackButton" Content="← Назад" VerticalAlignment="Center"
|
||||
Click="BackButton_Click" />
|
||||
<TextBlock Grid.Column="2" x:Name="PageTitleText" Text="Чудо Обувь" FontSize="26"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center" Margin="16,0" TextTrimming="CharacterEllipsis" />
|
||||
<StackPanel Grid.Column="3" Orientation="Vertical" VerticalAlignment="Top"
|
||||
HorizontalAlignment="Right" Margin="10,6">
|
||||
<TextBlock x:Name="UserNameText" FontSize="18" FontWeight="Bold"
|
||||
HorizontalAlignment="Right" />
|
||||
<TextBlock x:Name="UserRoleText" FontSize="14" HorizontalAlignment="Right" />
|
||||
<Button x:Name="LogoutButton" Content="Выйти" HorizontalAlignment="Right"
|
||||
Click="LogoutButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Frame Grid.Row="1" x:Name="MainFrame" NavigationUIVisibility="Hidden"
|
||||
Navigated="MainFrame_Navigated" />
|
||||
</Grid>
|
||||
</Window>
|
||||
79
ChudoObuv/MainWindow.xaml.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Navigation;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Pages;
|
||||
|
||||
namespace ChudoObuv
|
||||
{
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
MainFrame.Navigate(new LoginPage());
|
||||
}
|
||||
private void MainFrame_Navigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
// Заголовок окна и шапки берется из заголовка открытой страницы
|
||||
if (e.Content is Page page)
|
||||
{
|
||||
Title = $"Чудо Обувь — {page.Title}";
|
||||
PageTitleText.Text = page.Title;
|
||||
}
|
||||
BackButton.Visibility = MainFrame.CanGoBack ? Visibility.Visible : Visibility.Collapsed;
|
||||
UpdateUserInfo();
|
||||
}
|
||||
private void UpdateUserInfo()
|
||||
{
|
||||
bool isLoginPage = MainFrame.Content is LoginPage;
|
||||
if (Session.IsAuthorized)
|
||||
{
|
||||
UserNameText.Text = Session.CurrentUser.FullName;
|
||||
UserRoleText.Text = Session.CurrentUser.Role;
|
||||
|
||||
LogoutButton.Content = "Выйти";
|
||||
}
|
||||
else
|
||||
{
|
||||
UserNameText.Text = isLoginPage ? "" : "Гость";
|
||||
UserRoleText.Text = "";
|
||||
LogoutButton.Content = "Войти";
|
||||
}
|
||||
LogoutButton.Visibility = isLoginPage ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
private void BackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainFrame.CanGoBack)
|
||||
{
|
||||
MainFrame.GoBack();
|
||||
}
|
||||
}
|
||||
private void LogoutButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Session.Cart.Count > 0 && !Messages.Confirm("Неподтвержденный заказ будет отменен. Выйти из системы?"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Session.Logout();
|
||||
MainFrame.Navigate(new LoginPage());
|
||||
ClearHistory();
|
||||
}
|
||||
/// <summary>
|
||||
/// Очищает историю переходов, чтобы кнопкой «Назад» нельзя было вернуться в чужой сеанс.
|
||||
/// </summary>
|
||||
public void ClearHistory()
|
||||
{
|
||||
// Навигация выполняется асинхронно, поэтому очищаем журнал после завершения перехода
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
while (MainFrame.CanGoBack)
|
||||
{
|
||||
MainFrame.RemoveBackEntry();
|
||||
}
|
||||
BackButton.Visibility = Visibility.Collapsed;
|
||||
UpdateUserInfo();
|
||||
}), System.Windows.Threading.DispatcherPriority.ContextIdle);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ChudoObuv/Models/Order.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChudoObuv.Models
|
||||
{
|
||||
public class Order
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public DateTime OrderDate { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
public string ClientFullName { get; set; }
|
||||
public int ItemsCount { get; set; }
|
||||
public decimal TotalSum { get; set; }
|
||||
}
|
||||
}
|
||||
25
ChudoObuv/Models/OrderItem.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChudoObuv.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Строка состава заказа (сохраненная в БД или в оформляемом заказе).
|
||||
/// </summary>
|
||||
public class OrderItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int StockItemId { get; set; }
|
||||
public string ProductName { get; set; }
|
||||
public string Manufacturer { get; set; }
|
||||
public decimal Size { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
public string Position => $"{ProductName}, {Manufacturer}, размер {Size:0.#}";
|
||||
public string SizeText => Size.ToString("0.#");
|
||||
public decimal Sum => UnitPrice * Quantity;
|
||||
}
|
||||
}
|
||||
55
ChudoObuv/Models/Product.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using ChudoObuv.Logic;
|
||||
|
||||
namespace ChudoObuv.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Товар (модель обуви) для вывода в каталоге.
|
||||
/// </summary>
|
||||
public class Product
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Category { get; set; }
|
||||
public string Subcategory { get; set; }
|
||||
public string Manufacturer { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Composition { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public string ImageFile { get; set; }
|
||||
public int TotalQuantity { get; set; }
|
||||
public bool HasOrdersInPreviousMonth { get; set; }
|
||||
public string Title => $"{Manufacturer} | {Name}";
|
||||
public decimal DiscountedPrice => PriceCalculator.CalculateDiscountedPrice(Price,
|
||||
HasOrdersInPreviousMonth);
|
||||
public bool HasDiscount => DiscountedPrice < Price;
|
||||
public string QuantityLabel => PriceCalculator.GetQuantityLabel(TotalQuantity);
|
||||
public bool IsLowStock => PriceCalculator.IsLowStock(TotalQuantity);
|
||||
/// <summary>
|
||||
/// Путь к изображению товара; если файла нет — картинка-заглушка.
|
||||
/// </summary>
|
||||
public string ImagePath
|
||||
{
|
||||
get
|
||||
{
|
||||
string folder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources");
|
||||
if (!string.IsNullOrWhiteSpace(ImageFile))
|
||||
{
|
||||
string path = Path.Combine(folder, "Images", ImageFile);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return Path.Combine(folder, "picture.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
ChudoObuv/Models/StockItem.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChudoObuv.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Товарная позиция: модель + размер с доступным количеством.
|
||||
/// </summary>
|
||||
public class StockItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ProductId { get; set; }
|
||||
public decimal Size { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string SizeText => Size.ToString("0.#");
|
||||
}
|
||||
}
|
||||
24
ChudoObuv/Models/User.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ChudoObuv.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public const string AdminRole = "Администратор";
|
||||
public const string ManagerRole = "Менеджер";
|
||||
public const string ClientRole = "Авторизованный пользователь";
|
||||
public int Id { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string Patronymic { get; set; }
|
||||
public string Login { get; set; }
|
||||
public string Role { get; set; }
|
||||
public string FullName => $"{LastName} {FirstName} {Patronymic}".Trim();
|
||||
public bool IsAdmin => Role == AdminRole;
|
||||
public bool IsManagerOrAdmin => Role == ManagerRole || Role == AdminRole;
|
||||
}
|
||||
}
|
||||
45
ChudoObuv/Pages/CartPage.xaml
Normal file
@ -0,0 +1,45 @@
|
||||
<Page x:Class="ChudoObuv.Pages.CartPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Оформление заказа"
|
||||
Loaded="Page_Loaded">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" x:Name="ClientText" FontSize="18" Margin="4" TextWrapping="Wrap" />
|
||||
<DataGrid Grid.Row="1" x:Name="ItemsDataGrid">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Товар" Binding="{Binding ProductName}" Width="*" />
|
||||
<DataGridTextColumn Header="Производство" Binding="{Binding Manufacturer}" Width="170"
|
||||
/>
|
||||
<DataGridTextColumn Header="Размер" Binding="{Binding SizeText}" Width="80" />
|
||||
<DataGridTextColumn Header="Количество" Binding="{Binding Quantity}" Width="100" />
|
||||
<DataGridTextColumn Header="Цена за ед., ₽" Binding="{Binding UnitPrice,
|
||||
StringFormat=N2}" Width="120" />
|
||||
<DataGridTextColumn Header="Сумма, ₽" Binding="{Binding Sum, StringFormat=N2}"
|
||||
Width="120" />
|
||||
<DataGridTemplateColumn Header="" Width="110">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="Удалить" Margin="2" Padding="6,0" MinHeight="26"
|
||||
Click="RemoveItemButton_Click" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<TextBlock Grid.Row="2" x:Name="TotalText" FontSize="22" FontWeight="Bold"
|
||||
HorizontalAlignment="Right" Margin="4,8" />
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="Продолжить выбор товаров" Click="ContinueButton_Click" />
|
||||
<Button Content="Отказаться от заказа" Click="CancelOrderButton_Click" />
|
||||
<Button Content="Подтвердить заказ" Style="{StaticResource AccentButton}"
|
||||
Click="ConfirmButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
102
ChudoObuv/Pages/CartPage.xaml.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using ChudoObuv.Pages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class CartPage : Page
|
||||
{
|
||||
public CartPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshCart();
|
||||
}
|
||||
private void RefreshCart()
|
||||
{
|
||||
ClientText.Text = $"Клиент: {Session.OrderClient.FullName} Дата заказа: { DateTime.Today:dd.MM.yyyy}";
|
||||
ItemsDataGrid.ItemsSource = null;
|
||||
ItemsDataGrid.ItemsSource = Session.Cart;
|
||||
TotalText.Text = $"Итого: {Session.Cart.Sum(item => item.Sum):N2} ₽";
|
||||
}
|
||||
private void RemoveItemButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (((Button)sender).DataContext is OrderItem item &&
|
||||
Messages.Confirm($"Удалить из заказа «{item.ProductName}», размер {item.SizeText}?"))
|
||||
{
|
||||
Session.Cart.Remove(item);
|
||||
RefreshCart();
|
||||
}
|
||||
}
|
||||
private void ContinueButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(new CatalogPage());
|
||||
}
|
||||
private void CancelOrderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!Messages.Confirm("Отказаться от заказа? Все выбранные товары будут удалены из заказа. Действие нельзя отменить."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Session.CancelOrder();
|
||||
GoHome();
|
||||
}
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Session.Cart.Count == 0)
|
||||
{
|
||||
|
||||
Messages.ShowWarning("В заказе нет товаров. Выберите товар в каталоге, укажите размер и количество и нажмите «Добавить в заказ».");
|
||||
return;
|
||||
}
|
||||
if (!Messages.Confirm($"Подтвердить заказ на сумму {Session.Cart.Sum(item => item.Sum):N2} ₽для клиента { Session.OrderClient.FullName}?"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
int orderId = DataService.CreateOrder(Session.OrderClient.Id, Session.Cart);
|
||||
Messages.ShowInfo($"Заказ No {orderId} оформлен {DateTime.Today:dd.MM.yyyy}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
return;
|
||||
}
|
||||
Session.CancelOrder();
|
||||
GoHome();
|
||||
}
|
||||
/// <summary>
|
||||
/// Менеджер и администратор возвращаются к списку заказов, клиент — в каталог.
|
||||
/// </summary>
|
||||
private void GoHome()
|
||||
{
|
||||
if (Session.CurrentUser.IsManagerOrAdmin)
|
||||
{
|
||||
NavigationService.Navigate(new OrdersPage());
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationService.Navigate(new CatalogPage());
|
||||
}
|
||||
((MainWindow)Application.Current.MainWindow).ClearHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
133
ChudoObuv/Pages/CatalogPage.xaml
Normal file
@ -0,0 +1,133 @@
|
||||
<Page x:Class="ChudoObuv.Pages.CatalogPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Каталог товаров"
|
||||
Loaded="Page_Loaded">
|
||||
<Grid Margin="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Поиск, фильтрация и сортировка: только для авторизованных пользователей -->
|
||||
<Grid Grid.Row="0" x:Name="FilterPanel">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="230" />
|
||||
<ColumnDefinition Width="230" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Text="Поиск по наименованию и описанию:" Margin="4,0" />
|
||||
<TextBox x:Name="SearchTextBox" TextChanged="Filter_Changed" MaxLength="100" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="Категория:" Margin="4,0" />
|
||||
<ComboBox x:Name="CategoryComboBox" SelectionChanged="Filter_Changed" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2">
|
||||
<TextBlock Text="Сортировка по цене:" Margin="4,0" />
|
||||
<ComboBox x:Name="SortComboBox" SelectionChanged="Filter_Changed">
|
||||
<ComboBoxItem Content="Без сортировки" IsSelected="True" />
|
||||
<ComboBoxItem Content="По возрастанию цены" />
|
||||
<ComboBoxItem Content="По убыванию цены" />
|
||||
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<!-- Действия с заказом -->
|
||||
<DockPanel Grid.Row="1" Margin="0,4">
|
||||
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||
<Button x:Name="CartButton" Style="{StaticResource AccentButton}"
|
||||
Click="CartButton_Click" />
|
||||
<Button x:Name="OrdersButton" Content="Список заказов" Click="OrdersButton_Click" />
|
||||
</StackPanel>
|
||||
<TextBlock x:Name="OrderClientText" VerticalAlignment="Center" Margin="4,0"
|
||||
FontStyle="Italic" TextWrapping="Wrap" />
|
||||
</DockPanel>
|
||||
<ListView Grid.Row="2" x:Name="ProductsListView"
|
||||
SelectionChanged="ProductsListView_SelectionChanged"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
BorderThickness="0">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderBrush="Black" BorderThickness="1" Padding="8" Margin="2">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background" Value="{StaticResource
|
||||
MainBackgroundBrush}" />
|
||||
<Style.Triggers>
|
||||
<!-- Остаток по всем размерам три пары и меньше — подсветка -->
|
||||
<DataTrigger Binding="{Binding IsLowStock}" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource
|
||||
LowStockBrush}" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<Grid>
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="170" />
|
||||
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Grid.Column="0" Source="{Binding ImagePath}" Height="110"
|
||||
Stretch="Uniform" Margin="0,0,12,0" />
|
||||
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Title}" FontSize="20" FontWeight="Bold"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,4,0,0" FontSize="17">
|
||||
<Run Text="Категория:" /> <Run Text="{Binding Category,
|
||||
Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock FontSize="17">
|
||||
|
||||
<Run Text="Количество:" /> <Run Text="{Binding QuantityLabel,
|
||||
Mode=OneWay}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
|
||||
|
||||
<TextBlock FontSize="15" TextWrapping="Wrap">
|
||||
<Run Text="Состав:" /> <Run Text="{Binding Composition,
|
||||
Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" VerticalAlignment="Center"
|
||||
HorizontalAlignment="Right">
|
||||
<TextBlock Text="{Binding Price, StringFormat={}{0:N2} ₽}" FontSize="16"
|
||||
TextDecorations="Strikethrough" Foreground="DimGray"
|
||||
HorizontalAlignment="Right">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
|
||||
<Style.Triggers>
|
||||
|
||||
<DataTrigger Binding="{Binding HasDiscount}"
|
||||
Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock Text="{Binding DiscountedPrice, StringFormat={}{0:N2} ₽}"
|
||||
|
||||
FontSize="24"
|
||||
FontWeight="Bold" HorizontalAlignment="Right" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<TextBlock Grid.Row="3" x:Name="CountText" Margin="4" Foreground="DimGray" />
|
||||
</Grid>
|
||||
</Page>
|
||||
131
ChudoObuv/Pages/CatalogPage.xaml.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class CatalogPage : Page
|
||||
{
|
||||
private const string AllCategories = "Все категории";
|
||||
private List<Product> allProducts = new List<Product>();
|
||||
private bool isInitialized;
|
||||
public CatalogPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
// Вызывается при каждом открытии страницы, в том числе после возврата «Назад»,
|
||||
// поэтому каталог и остатки всегда актуальны
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool isAuthorized = Session.IsAuthorized;
|
||||
FilterPanel.Visibility = isAuthorized ? Visibility.Visible : Visibility.Collapsed;
|
||||
CartButton.Visibility = isAuthorized ? Visibility.Visible : Visibility.Collapsed;
|
||||
OrdersButton.Visibility = isAuthorized && Session.CurrentUser.IsManagerOrAdmin ?
|
||||
Visibility.Visible : Visibility.Collapsed;
|
||||
CartButton.Content = $"Оформление заказа ({Session.Cart.Sum(item => item.Quantity)})";
|
||||
OrderClientText.Text = isAuthorized ? $"Заказ оформляется для клиента: {Session.OrderClient.FullName}" : "Для оформления заказа войдите в систему.";
|
||||
try
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
|
||||
|
||||
List<string> categories = DataService.GetCategories();
|
||||
|
||||
categories.Insert(0, AllCategories);
|
||||
CategoryComboBox.ItemsSource = categories;
|
||||
CategoryComboBox.SelectedIndex = 0;
|
||||
isInitialized = true;
|
||||
|
||||
}
|
||||
allProducts = DataService.GetProducts();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
allProducts = new List<Product>();
|
||||
}
|
||||
ApplyFilters();
|
||||
}
|
||||
private void Filter_Changed(object sender, EventArgs e)
|
||||
{
|
||||
ApplyFilters();
|
||||
}
|
||||
/// <summary>
|
||||
/// Поиск, фильтр и сортировка применяются вместе при любом изменении.
|
||||
/// </summary>
|
||||
private void ApplyFilters()
|
||||
{
|
||||
if (ProductsListView == null || SortComboBox == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
IEnumerable<Product> result = allProducts;
|
||||
if (Session.IsAuthorized)
|
||||
{
|
||||
string search = SearchTextBox.Text.Trim();
|
||||
if (search.Length > 0)
|
||||
{
|
||||
result = result.Where(product =>
|
||||
product.Name.Contains(search, StringComparison.OrdinalIgnoreCase) ||
|
||||
product.Description.Contains(search, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
}
|
||||
string category = CategoryComboBox.SelectedItem as string;
|
||||
if (category != null && category != AllCategories)
|
||||
{
|
||||
result = result.Where(product => product.Category == category);
|
||||
}
|
||||
if (SortComboBox.SelectedIndex == 1)
|
||||
{
|
||||
result = result.OrderBy(product => product.DiscountedPrice);
|
||||
}
|
||||
else if (SortComboBox.SelectedIndex == 2)
|
||||
{
|
||||
result = result.OrderByDescending(product => product.DiscountedPrice);
|
||||
}
|
||||
}
|
||||
List<Product> shown = result.ToList();
|
||||
ProductsListView.ItemsSource = shown;
|
||||
CountText.Text = $"Показано товаров: {shown.Count} из {allProducts.Count}";
|
||||
}
|
||||
private void ProductsListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (ProductsListView.SelectedItem is not Product product)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ProductsListView.SelectedItem = null;
|
||||
if (!Session.IsAuthorized)
|
||||
{
|
||||
|
||||
|
||||
Messages.ShowInfo("Оформлять заказы могут только авторизованные пользователи.\nВыйдите на страницу входа и войдите под своим логином.");
|
||||
return;
|
||||
}
|
||||
NavigationService.Navigate(new ProductPage(product));
|
||||
}
|
||||
private void CartButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(new CartPage());
|
||||
}
|
||||
private void OrdersButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(new OrdersPage());
|
||||
}
|
||||
}
|
||||
}
|
||||
26
ChudoObuv/Pages/LoginPage.xaml
Normal file
@ -0,0 +1,26 @@
|
||||
<Page x:Class="ChudoObuv.Pages.LoginPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Вход в систему">
|
||||
<Grid>
|
||||
<Border Width="420" VerticalAlignment="Center" HorizontalAlignment="Center" Padding="24"
|
||||
Background="{StaticResource SecondaryBackgroundBrush}" BorderBrush="{StaticResource
|
||||
AccentBrush}"
|
||||
BorderThickness="2" CornerRadius="8">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Вход в систему" FontSize="24" FontWeight="Bold"
|
||||
HorizontalAlignment="Center" Margin="0,0,0,16" />
|
||||
<TextBlock Text="Логин:" Margin="4,0" />
|
||||
<TextBox x:Name="LoginTextBox" MaxLength="50" KeyDown="LoginTextBox_KeyDown"
|
||||
ToolTip="Введите логин из списка пользователей системы. Пароль не требуется."
|
||||
/>
|
||||
<TextBlock Text="Пароль вводить не требуется." FontSize="13" Foreground="DimGray"
|
||||
Margin="4,0,4,12" />
|
||||
<Button Content="Войти" Style="{StaticResource AccentButton}" Click="LoginButton_Click"
|
||||
/>
|
||||
<Button Content="Войти как гость" Click="GuestButton_Click" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
||||
79
ChudoObuv/Pages/LoginPage.xaml.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class LoginPage : Page
|
||||
{
|
||||
public LoginPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += (sender, e) => LoginTextBox.Focus();
|
||||
}
|
||||
private void LoginButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TryLogin();
|
||||
}
|
||||
private void LoginTextBox_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
TryLogin();
|
||||
}
|
||||
}
|
||||
private void TryLogin()
|
||||
{
|
||||
string login = LoginTextBox.Text.Trim();
|
||||
if (login.Length == 0)
|
||||
{
|
||||
|
||||
Messages.ShowWarning("Введите логин. Логин выдается администратором и указан в списке пользователей системы.");
|
||||
LoginTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
User user;
|
||||
try
|
||||
{
|
||||
user = DataService.FindUserByLogin(login);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
return;
|
||||
}
|
||||
if (user == null)
|
||||
{
|
||||
Messages.ShowError($"Пользователь с логином «{login}» не найден.\nПроверьте раскладку клавиатуры и правильность ввода логина или войдите как гость.");
|
||||
LoginTextBox.SelectAll();
|
||||
LoginTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
Session.Login(user);
|
||||
OpenCatalog();
|
||||
}
|
||||
private void GuestButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Session.Logout();
|
||||
OpenCatalog();
|
||||
}
|
||||
private void OpenCatalog()
|
||||
{
|
||||
NavigationService.Navigate(new CatalogPage());
|
||||
((MainWindow)Application.Current.MainWindow).ClearHistory();
|
||||
}
|
||||
}
|
||||
}
|
||||
47
ChudoObuv/Pages/OrderPage.xaml
Normal file
@ -0,0 +1,47 @@
|
||||
<Page x:Class="ChudoObuv.Pages.OrderPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Состав заказа"
|
||||
Loaded="Page_Loaded">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Grid.Row="0" x:Name="HeaderText" FontSize="20" FontWeight="Bold" Margin="4"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,4">
|
||||
<TextBlock Text="Дата заказа:" VerticalAlignment="Center" Margin="4,0" />
|
||||
<TextBlock x:Name="OrderDateText" VerticalAlignment="Center" FontWeight="Bold" />
|
||||
<!-- Редактирование даты доступно только администратору -->
|
||||
<DatePicker x:Name="OrderDatePicker" Width="160" Margin="4" FontSize="16" />
|
||||
<Button x:Name="SaveDateButton" Content="Сохранить дату" Style="{StaticResource
|
||||
AccentButton}" Click="SaveDateButton_Click" />
|
||||
</StackPanel>
|
||||
<DataGrid Grid.Row="2" x:Name="ItemsDataGrid">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Товарная позиция (модель, производство, размер)"
|
||||
Binding="{Binding Position}" Width="*" />
|
||||
<DataGridTextColumn Header="Количество" Binding="{Binding Quantity}" Width="110" />
|
||||
<DataGridTextColumn Header="Цена за ед., ₽" Binding="{Binding UnitPrice,
|
||||
StringFormat=N2}" Width="130" />
|
||||
<DataGridTextColumn Header="Сумма, ₽" Binding="{Binding Sum, StringFormat=N2}"
|
||||
Width="130" />
|
||||
<DataGridTemplateColumn x:Name="DeleteColumn" Header="" Width="170">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="Удалить позицию" Margin="2" Padding="6,0" MinHeight="26"
|
||||
Click="DeleteItemButton_Click" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<TextBlock Grid.Row="3" x:Name="TotalText" FontSize="22" FontWeight="Bold"
|
||||
HorizontalAlignment="Right" Margin="4,8" />
|
||||
</Grid>
|
||||
</Page>
|
||||
109
ChudoObuv/Pages/OrderPage.xaml.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Globalization;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class OrderPage : Page
|
||||
{
|
||||
private readonly Order order;
|
||||
private List<OrderItem> items = new List<OrderItem>();
|
||||
public OrderPage(Order order)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.order = order;
|
||||
Title = $"Состав заказа No {order.Id}";
|
||||
}
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
bool isAdmin = Session.CurrentUser.IsAdmin;
|
||||
OrderDateText.Visibility = isAdmin ? Visibility.Collapsed : Visibility.Visible;
|
||||
OrderDatePicker.Visibility = isAdmin ? Visibility.Visible : Visibility.Collapsed;
|
||||
SaveDateButton.Visibility = isAdmin ? Visibility.Visible : Visibility.Collapsed;
|
||||
DeleteColumn.Visibility = isAdmin ? Visibility.Visible : Visibility.Collapsed;
|
||||
HeaderText.Text = $"Заказ No {order.Id}. Клиент: {order.ClientFullName}";
|
||||
OrderDateText.Text = order.OrderDate.ToString("dd.MM.yyyy");
|
||||
OrderDatePicker.SelectedDate = order.OrderDate;
|
||||
LoadItems();
|
||||
}
|
||||
private void LoadItems()
|
||||
{
|
||||
try
|
||||
{
|
||||
items = DataService.GetOrderItems(order.Id);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
items = new List<OrderItem>();
|
||||
}
|
||||
ItemsDataGrid.ItemsSource = items;
|
||||
TotalText.Text = $"Итоговая сумма по заказу: {items.Sum(item => item.Sum):N2} ₽";
|
||||
}
|
||||
private void SaveDateButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Дату, введенную с клавиатуры, разбираем сами: DatePicker применяет ее только при потере фокуса
|
||||
if (!DateTime.TryParseExact(OrderDatePicker.Text.Trim(), "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime newDate))
|
||||
{
|
||||
Messages.ShowWarning("Дата заказа указана неверно. Выберите ее в календаре или введите в формате ДД.ММ.ГГГГ, например 15.04.2026.");
|
||||
OrderDatePicker.Focus();
|
||||
return;
|
||||
}
|
||||
if (newDate.Date > DateTime.Today)
|
||||
{
|
||||
Messages.ShowWarning("Дата заказа не может быть позже сегодняшней. Выберите другую дату.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
DataService.UpdateOrderDate(order.Id, newDate);
|
||||
order.OrderDate = newDate;
|
||||
Messages.ShowInfo($"Дата заказа No {order.Id} изменена на {newDate:dd.MM.yyyy}.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
}
|
||||
}
|
||||
private void DeleteItemButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (((Button)sender).DataContext is not OrderItem item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (items.Count == 1)
|
||||
{
|
||||
Messages.ShowWarning("Это последняя позиция заказа. Пустой заказ хранить нельзя — удалите весь заказ на странице «Список заказов».");
|
||||
return;
|
||||
}
|
||||
if (!Messages.Confirm($"Удалить позицию «{item.Position}» из заказа No {order.Id}?\nТовар вернется на склад.Действие нельзя отменить."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
DataService.DeleteOrderItem(item.Id);
|
||||
LoadItems();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
43
ChudoObuv/Pages/OrdersPage.xaml
Normal file
@ -0,0 +1,43 @@
|
||||
<Page x:Class="ChudoObuv.Pages.OrdersPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Список заказов"
|
||||
Loaded="Page_Loaded">
|
||||
<Grid Margin="12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Новый заказ: сначала выбирается клиент -->
|
||||
<Border Grid.Row="0" Background="{StaticResource SecondaryBackgroundBrush}" Padding="8"
|
||||
CornerRadius="6" Margin="0,0,0,8">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Новый заказ для клиента:" VerticalAlignment="Center" Margin="4,0" />
|
||||
<ComboBox x:Name="ClientComboBox" Width="320" DisplayMemberPath="FullName" />
|
||||
<Button Content="Добавить заказ" Style="{StaticResource AccentButton}"
|
||||
Click="AddOrderButton_Click" />
|
||||
<Button Content="Каталог товаров" Click="CatalogButton_Click" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<DataGrid Grid.Row="1" x:Name="OrdersDataGrid"
|
||||
MouseDoubleClick="OrdersDataGrid_MouseDoubleClick">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="No заказа" Binding="{Binding Id}" Width="90" />
|
||||
<DataGridTextColumn Header="Дата заказа" Binding="{Binding OrderDate,
|
||||
StringFormat=dd.MM.yyyy}" Width="130" />
|
||||
<DataGridTextColumn Header="ФИО клиента" Binding="{Binding ClientFullName}" Width="*" />
|
||||
<DataGridTextColumn Header="Позиций" Binding="{Binding ItemsCount}" Width="90" />
|
||||
<DataGridTextColumn Header="Сумма, ₽" Binding="{Binding TotalSum, StringFormat=N2}"
|
||||
Width="130" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<TextBlock Text="Двойной щелчок по заказу — просмотр состава" VerticalAlignment="Center"
|
||||
Foreground="DimGray" Margin="8,0" />
|
||||
<Button Content="Состав заказа" Click="OpenOrderButton_Click" />
|
||||
<Button Content="Удалить заказ" Click="DeleteOrderButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
111
ChudoObuv/Pages/OrdersPage.xaml.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class OrdersPage : Page
|
||||
{
|
||||
public OrdersPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
|
||||
}
|
||||
// Список перечитывается при каждом открытии страницы (после добавления, изменения, удаления)
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ClientComboBox.ItemsSource == null)
|
||||
{
|
||||
ClientComboBox.ItemsSource = DataService.GetClients();
|
||||
}
|
||||
OrdersDataGrid.ItemsSource = DataService.GetOrders();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
}
|
||||
}
|
||||
private Order GetSelectedOrder()
|
||||
{
|
||||
if (OrdersDataGrid.SelectedItem is Order order)
|
||||
{
|
||||
return order;
|
||||
}
|
||||
Messages.ShowWarning("Сначала выберите заказ в списке (щелкните по строке).");
|
||||
return null;
|
||||
}
|
||||
private void AddOrderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ClientComboBox.SelectedItem is not User client)
|
||||
{
|
||||
Messages.ShowWarning("Выберите клиента, для которого оформляется заказ.");
|
||||
ClientComboBox.Focus();
|
||||
return;
|
||||
}
|
||||
if (Session.Cart.Count > 0 && !Messages.Confirm("Ранее начатый неподтвержденный заказ будет отменен.Продолжить ? "))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Session.StartOrderFor(client);
|
||||
NavigationService.Navigate(new CatalogPage());
|
||||
}
|
||||
private void CatalogButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(new CatalogPage());
|
||||
}
|
||||
private void OpenOrderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Order order = GetSelectedOrder();
|
||||
if (order != null)
|
||||
{
|
||||
NavigationService.Navigate(new OrderPage(order));
|
||||
}
|
||||
}
|
||||
private void OrdersDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (OrdersDataGrid.SelectedItem is Order order)
|
||||
{
|
||||
NavigationService.Navigate(new OrderPage(order));
|
||||
}
|
||||
}
|
||||
private void DeleteOrderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Order order = GetSelectedOrder();
|
||||
if (order == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Messages.Confirm($"Удалить заказ No {order.Id} от {order.OrderDate:dd.MM.yyyy} ({ order.ClientFullName})?\n" + "Товары вернутся на склад. Действие нельзя отменить."))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
DataService.DeleteOrder(order.Id);
|
||||
OrdersDataGrid.ItemsSource = DataService.GetOrders();
|
||||
Messages.ShowInfo($"Заказ No {order.Id} удален.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
76
ChudoObuv/Pages/ProductPage.xaml
Normal file
@ -0,0 +1,76 @@
|
||||
<Page x:Class="ChudoObuv.Pages.ProductPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
FontFamily="Calibri" FontSize="16" Background="{StaticResource MainBackgroundBrush}"
|
||||
Title="Просмотр товара"
|
||||
Loaded="Page_Loaded">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid Margin="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="340" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" BorderBrush="{StaticResource AccentBrush}" BorderThickness="1"
|
||||
Padding="8" VerticalAlignment="Top">
|
||||
<Image x:Name="ProductImage" Height="280" Stretch="Uniform" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Margin="20,0,0,0">
|
||||
<TextBlock x:Name="NameText" FontSize="26" FontWeight="Bold" TextWrapping="Wrap" />
|
||||
<TextBlock Margin="0,6,0,0"><Run Text="Производство:" FontWeight="Bold" /> <Run
|
||||
x:Name="ManufacturerRun" /></TextBlock>
|
||||
<TextBlock><Run Text="Категория:" FontWeight="Bold" /> <Run x:Name="CategoryRun"
|
||||
/></TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,6">
|
||||
<TextBlock Text="Цена со скидкой:" FontWeight="Bold" VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="DiscountedPriceText" FontSize="24" FontWeight="Bold" Margin="8,0"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock x:Name="OldPriceText" TextDecorations="Strikethrough"
|
||||
Foreground="DimGray" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<TextBlock TextWrapping="Wrap"><Run Text="Состав:" FontWeight="Bold" /> <Run
|
||||
x:Name="CompositionRun" /></TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,6,0,0"><Run Text="Описание:" FontWeight="Bold"
|
||||
/> <Run x:Name="DescriptionRun" /></TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,6,0,0"><Run Text="Размерный ряд:"
|
||||
FontWeight="Bold" /> <Run x:Name="SizeRangeRun" /></TextBlock>
|
||||
<TextBlock TextWrapping="Wrap"><Run Text="Доступные для заказа размеры:"
|
||||
FontWeight="Bold" /> <Run x:Name="AvailableSizesRun" /></TextBlock>
|
||||
<Border Background="{StaticResource SecondaryBackgroundBrush}" Padding="12"
|
||||
Margin="0,16,0,0" CornerRadius="6">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Добавление в заказ" FontSize="20" FontWeight="Bold" />
|
||||
<Grid Margin="0,8,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="160" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="Размер:" VerticalAlignment="Center" />
|
||||
<ComboBox Grid.Column="1" x:Name="SizeComboBox" DisplayMemberPath="SizeText"
|
||||
|
||||
SelectionChanged="SizeComboBox_SelectionChanged" />
|
||||
<TextBlock Grid.Column="2" Text="Количество:" VerticalAlignment="Center"
|
||||
Margin="12,0,0,0" />
|
||||
<TextBox Grid.Column="3" x:Name="QuantityTextBox" Text="1" MaxLength="3"
|
||||
PreviewTextInput="QuantityTextBox_PreviewTextInput"
|
||||
|
||||
ToolTip="Введите целое число пар от 1 до доступного количества" />
|
||||
<TextBlock Grid.Column="4" x:Name="AvailableText" VerticalAlignment="Center"
|
||||
Margin="8,0" Foreground="DimGray" />
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,8,0,0">
|
||||
|
||||
<Button Content="Добавить в заказ" Style="{StaticResource AccentButton}"
|
||||
Click="AddButton_Click" />
|
||||
<Button Content="Перейти к оформлению заказа" Click="CartButton_Click" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Page>
|
||||
126
ChudoObuv/Pages/ProductPage.xaml.cs
Normal file
@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using ChudoObuv.Data;
|
||||
using ChudoObuv.Logic;
|
||||
using ChudoObuv.Models;
|
||||
namespace ChudoObuv.Pages
|
||||
{
|
||||
public partial class ProductPage : Page
|
||||
{
|
||||
private readonly Product product;
|
||||
private List<StockItem> stockItems = new List<StockItem>();
|
||||
public ProductPage(Product product)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.product = product;
|
||||
Title = $"Просмотр товара: {product.Name}";
|
||||
}
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ProductImage.Source = new BitmapImage(new Uri(product.ImagePath));
|
||||
NameText.Text = product.Name;
|
||||
ManufacturerRun.Text = product.Manufacturer;
|
||||
CategoryRun.Text = $"{product.Category} / {product.Subcategory}";
|
||||
DiscountedPriceText.Text = $"{product.DiscountedPrice:N2} ₽";
|
||||
OldPriceText.Text = product.HasDiscount ? $"{product.Price:N2} ₽" : "";
|
||||
CompositionRun.Text = product.Composition;
|
||||
DescriptionRun.Text = product.Description;
|
||||
LoadSizes();
|
||||
}
|
||||
/// <summary>
|
||||
/// Загружает актуальные остатки из БД и учитывает то, что уже добавлено в заказ.
|
||||
/// </summary>
|
||||
private void LoadSizes()
|
||||
{
|
||||
try
|
||||
{
|
||||
stockItems = DataService.GetStockItems(product.Id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Messages.ShowDatabaseError(ex);
|
||||
stockItems = new List<StockItem>();
|
||||
}
|
||||
SizeRangeRun.Text = stockItems.Count > 0 ? string.Join(", ", stockItems.Select(item =>
|
||||
item.SizeText)) : "нет данных";
|
||||
List<StockItem> available = stockItems
|
||||
.Where(item => item.Quantity - Session.GetQuantityInCart(item.Id) > 0)
|
||||
.ToList();
|
||||
AvailableSizesRun.Text = available.Count > 0 ? string.Join(", ", available.Select(item =>
|
||||
item.SizeText)) : "нет в наличии";
|
||||
SizeComboBox.ItemsSource = available;
|
||||
SizeComboBox.SelectedIndex = available.Count > 0 ? 0 : -1;
|
||||
UpdateAvailableText();
|
||||
}
|
||||
private int GetAvailableQuantity(StockItem stockItem)
|
||||
{
|
||||
return stockItem.Quantity - Session.GetQuantityInCart(stockItem.Id);
|
||||
}
|
||||
private void SizeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
|
||||
{
|
||||
UpdateAvailableText();
|
||||
}
|
||||
private void UpdateAvailableText()
|
||||
{
|
||||
AvailableText.Text = SizeComboBox.SelectedItem is StockItem stockItem
|
||||
? $"доступно: {GetAvailableQuantity(stockItem)} пар"
|
||||
: "";
|
||||
}
|
||||
// В поле количества можно вводить только цифры
|
||||
private void QuantityTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
|
||||
{
|
||||
e.Handled = !e.Text.All(char.IsDigit);
|
||||
}
|
||||
private void AddButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (SizeComboBox.SelectedItem is not StockItem stockItem)
|
||||
{
|
||||
Messages.ShowWarning("Выберите размер обуви. Если список размеров пуст — товара нет в наличии.");
|
||||
return;
|
||||
}
|
||||
int available = GetAvailableQuantity(stockItem);
|
||||
if (!int.TryParse(QuantityTextBox.Text, out int quantity) || quantity <= 0)
|
||||
{
|
||||
Messages.ShowWarning("Количество указано неверно. Введите целое число пар больше нуля.");
|
||||
QuantityTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
if (quantity > available)
|
||||
{
|
||||
Messages.ShowWarning($"Для размера {stockItem.SizeText} доступно только {available} пар. Уменьшите количество.");
|
||||
QuantityTextBox.Focus();
|
||||
return;
|
||||
}
|
||||
Session.AddToCart(new OrderItem
|
||||
{
|
||||
StockItemId = stockItem.Id,
|
||||
ProductName = product.Name,
|
||||
Manufacturer = product.Manufacturer,
|
||||
Size = stockItem.Size,
|
||||
Quantity = quantity,
|
||||
UnitPrice = product.DiscountedPrice
|
||||
});
|
||||
Messages.ShowInfo($"Товар «{product.Name}», размер {stockItem.SizeText}, {quantity} пар добавлен в заказ.\n" +
|
||||
"Добавьте другие товары или перейдите к оформлению заказа.");
|
||||
QuantityTextBox.Text = "1";
|
||||
LoadSizes();
|
||||
}
|
||||
private void CartButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NavigationService.Navigate(new CartPage());
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ChudoObuv/Resources/Images/IMG_KB_164811.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KB_164816.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KB_185501.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KS_195213.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KS_195217.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KS_195222.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KSd_185508.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KSd_195207.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
ChudoObuv/Resources/Images/IMG_KSho_185512.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MB_174349.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MB_191200.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MB_191205.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MS_185531.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MS_185532.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MS_185533.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MSho_190514.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MSho_190518.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
ChudoObuv/Resources/Images/IMG_MSho_191612.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WB_170859.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WB_170905.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WB_170906.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WS_174110.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WS_185528.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WS_185535.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WS_185538.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSd_174332.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSd_185156.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSd_185551.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSho_174409.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSho_185202.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
ChudoObuv/Resources/Images/IMG_WSho_185545.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
ChudoObuv/Resources/app.ico
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
ChudoObuv/Resources/logo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
ChudoObuv/Resources/picture.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
@ -0,0 +1,78 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v8.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v8.0": {
|
||||
"ChudoObuv/1.0.0": {
|
||||
"dependencies": {
|
||||
"MySqlConnector": "2.6.2"
|
||||
},
|
||||
"runtime": {
|
||||
"ChudoObuv.dll": {}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": {
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||
"assemblyVersion": "8.0.0.0",
|
||||
"fileVersion": "8.0.1024.46610"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/8.0.2": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||
"assemblyVersion": "8.0.0.0",
|
||||
"fileVersion": "8.0.1024.46610"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MySqlConnector/2.6.2": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.2"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/MySqlConnector.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"ChudoObuv/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions/8.0.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
|
||||
"path": "microsoft.extensions.dependencyinjection.abstractions/8.0.2",
|
||||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.8.0.2.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions/8.0.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
|
||||
"path": "microsoft.extensions.logging.abstractions/8.0.2",
|
||||
"hashPath": "microsoft.extensions.logging.abstractions.8.0.2.nupkg.sha512"
|
||||
},
|
||||
"MySqlConnector/2.6.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-dlTc/tsBa42szdvpj4nrHvQVUb0gsMwR9HT6J0Zz2OSqfP0CdYlGw+qQPqCpbnO97idfvclSjKYtM6y5QaBQaQ==",
|
||||
"path": "mysqlconnector/2.6.2",
|
||||
"hashPath": "mysqlconnector.2.6.2.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.dll
Normal file
BIN
ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.exe
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net8.0",
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "8.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.WindowsDesktop.App",
|
||||
"version": "8.0.0"
|
||||
}
|
||||
],
|
||||
"configProperties": {
|
||||
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
||||
"CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ChudoObuv/Исполняемые_файлы/net8.0-windows/MySqlConnector.dll
Normal file
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 24 KiB |