From 556a3a6c887e564db7459357ce959616c9a411a8 Mon Sep 17 00:00:00 2001 From: homyachelus Date: Sat, 31 Oct 2026 19:36:02 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D1=8C=D1=82?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChudoObuv.slnx | 3 + ChudoObuv/App.xaml | 57 ++++ ChudoObuv/App.xaml.cs | 32 ++ ChudoObuv/AssemblyInfo.cs | 10 + ChudoObuv/ChudoObuv.csproj | 24 ++ ChudoObuv/Data/DataService.cs | 321 ++++++++++++++++++ ChudoObuv/Data/Database.cs | 24 ++ ChudoObuv/Logic/Messages.cs | 56 +++ ChudoObuv/Logic/PriceCalculator.cs | 51 +++ ChudoObuv/Logic/Session.cs | 65 ++++ ChudoObuv/MainWindow.xaml | 43 +++ ChudoObuv/MainWindow.xaml.cs | 79 +++++ ChudoObuv/Models/Order.cs | 18 + ChudoObuv/Models/OrderItem.cs | 25 ++ ChudoObuv/Models/Product.cs | 55 +++ ChudoObuv/Models/StockItem.cs | 20 ++ ChudoObuv/Models/User.cs | 24 ++ ChudoObuv/Pages/CartPage.xaml | 45 +++ ChudoObuv/Pages/CartPage.xaml.cs | 102 ++++++ ChudoObuv/Pages/CatalogPage.xaml | 133 ++++++++ ChudoObuv/Pages/CatalogPage.xaml.cs | 131 +++++++ ChudoObuv/Pages/LoginPage.xaml | 26 ++ ChudoObuv/Pages/LoginPage.xaml.cs | 79 +++++ ChudoObuv/Pages/OrderPage.xaml | 47 +++ ChudoObuv/Pages/OrderPage.xaml.cs | 109 ++++++ ChudoObuv/Pages/OrdersPage.xaml | 43 +++ ChudoObuv/Pages/OrdersPage.xaml.cs | 111 ++++++ ChudoObuv/Pages/ProductPage.xaml | 76 +++++ ChudoObuv/Pages/ProductPage.xaml.cs | 126 +++++++ ChudoObuv/Resources/Images/IMG_KB_164811.png | Bin 0 -> 39959 bytes ChudoObuv/Resources/Images/IMG_KB_164816.png | Bin 0 -> 54817 bytes ChudoObuv/Resources/Images/IMG_KB_185501.png | Bin 0 -> 47054 bytes ChudoObuv/Resources/Images/IMG_KS_195213.png | Bin 0 -> 41326 bytes ChudoObuv/Resources/Images/IMG_KS_195217.png | Bin 0 -> 22211 bytes ChudoObuv/Resources/Images/IMG_KS_195222.png | Bin 0 -> 42832 bytes ChudoObuv/Resources/Images/IMG_KSd_185508.png | Bin 0 -> 22160 bytes ChudoObuv/Resources/Images/IMG_KSd_195207.png | Bin 0 -> 39348 bytes .../Resources/Images/IMG_KSho_185512.png | Bin 0 -> 24290 bytes ChudoObuv/Resources/Images/IMG_MB_174349.png | Bin 0 -> 26109 bytes ChudoObuv/Resources/Images/IMG_MB_191200.png | Bin 0 -> 25333 bytes ChudoObuv/Resources/Images/IMG_MB_191205.png | Bin 0 -> 39891 bytes ChudoObuv/Resources/Images/IMG_MS_185531.png | Bin 0 -> 29802 bytes ChudoObuv/Resources/Images/IMG_MS_185532.png | Bin 0 -> 36333 bytes ChudoObuv/Resources/Images/IMG_MS_185533.png | Bin 0 -> 40807 bytes .../Resources/Images/IMG_MSho_190514.png | Bin 0 -> 24665 bytes .../Resources/Images/IMG_MSho_190518.png | Bin 0 -> 22809 bytes .../Resources/Images/IMG_MSho_191612.png | Bin 0 -> 25872 bytes ChudoObuv/Resources/Images/IMG_WB_170859.png | Bin 0 -> 30846 bytes ChudoObuv/Resources/Images/IMG_WB_170905.png | Bin 0 -> 21495 bytes ChudoObuv/Resources/Images/IMG_WB_170906.png | Bin 0 -> 27887 bytes ChudoObuv/Resources/Images/IMG_WS_174110.png | Bin 0 -> 48456 bytes ChudoObuv/Resources/Images/IMG_WS_185528.png | Bin 0 -> 40901 bytes ChudoObuv/Resources/Images/IMG_WS_185535.png | Bin 0 -> 21129 bytes ChudoObuv/Resources/Images/IMG_WS_185538.png | Bin 0 -> 32047 bytes ChudoObuv/Resources/Images/IMG_WSd_174332.png | Bin 0 -> 30501 bytes ChudoObuv/Resources/Images/IMG_WSd_185156.png | Bin 0 -> 30642 bytes ChudoObuv/Resources/Images/IMG_WSd_185551.png | Bin 0 -> 28824 bytes .../Resources/Images/IMG_WSho_174409.png | Bin 0 -> 29066 bytes .../Resources/Images/IMG_WSho_185202.png | Bin 0 -> 24431 bytes .../Resources/Images/IMG_WSho_185545.png | Bin 0 -> 20860 bytes ChudoObuv/Resources/app.ico | Bin 0 -> 21476 bytes ChudoObuv/Resources/logo.png | Bin 0 -> 23776 bytes ChudoObuv/Resources/picture.png | Bin 0 -> 36311 bytes .../net8.0-windows/ChudoObuv.deps.json | 78 +++++ .../net8.0-windows/ChudoObuv.dll | Bin 0 -> 137728 bytes .../net8.0-windows/ChudoObuv.exe | Bin 0 -> 173056 bytes .../net8.0-windows/ChudoObuv.runtimeconfig.json | 19 ++ .../Microsoft.Extensions.DependencyInjection.Abstractions.dll | Bin 0 -> 63768 bytes .../Microsoft.Extensions.Logging.Abstractions.dll | Bin 0 -> 65320 bytes .../net8.0-windows/MySqlConnector.dll | Bin 0 -> 881000 bytes .../net8.0-windows/Resources/Images/IMG_KB_164811.png | Bin 0 -> 39959 bytes .../net8.0-windows/Resources/Images/IMG_KB_164816.png | Bin 0 -> 54817 bytes .../net8.0-windows/Resources/Images/IMG_KB_185501.png | Bin 0 -> 47054 bytes .../net8.0-windows/Resources/Images/IMG_KS_195213.png | Bin 0 -> 41326 bytes .../net8.0-windows/Resources/Images/IMG_KS_195217.png | Bin 0 -> 22211 bytes .../net8.0-windows/Resources/Images/IMG_KS_195222.png | Bin 0 -> 42832 bytes .../net8.0-windows/Resources/Images/IMG_KSd_185508.png | Bin 0 -> 22160 bytes .../net8.0-windows/Resources/Images/IMG_KSd_195207.png | Bin 0 -> 39348 bytes .../net8.0-windows/Resources/Images/IMG_KSho_185512.png | Bin 0 -> 24290 bytes .../net8.0-windows/Resources/Images/IMG_MB_174349.png | Bin 0 -> 26109 bytes .../net8.0-windows/Resources/Images/IMG_MB_191200.png | Bin 0 -> 25333 bytes .../net8.0-windows/Resources/Images/IMG_MB_191205.png | Bin 0 -> 39891 bytes .../net8.0-windows/Resources/Images/IMG_MS_185531.png | Bin 0 -> 29802 bytes .../net8.0-windows/Resources/Images/IMG_MS_185532.png | Bin 0 -> 36333 bytes .../net8.0-windows/Resources/Images/IMG_MS_185533.png | Bin 0 -> 40807 bytes .../net8.0-windows/Resources/Images/IMG_MSho_190514.png | Bin 0 -> 24665 bytes .../net8.0-windows/Resources/Images/IMG_MSho_190518.png | Bin 0 -> 22809 bytes .../net8.0-windows/Resources/Images/IMG_MSho_191612.png | Bin 0 -> 25872 bytes .../net8.0-windows/Resources/Images/IMG_WB_170859.png | Bin 0 -> 30846 bytes .../net8.0-windows/Resources/Images/IMG_WB_170905.png | Bin 0 -> 21495 bytes .../net8.0-windows/Resources/Images/IMG_WB_170906.png | Bin 0 -> 27887 bytes .../net8.0-windows/Resources/Images/IMG_WS_174110.png | Bin 0 -> 48456 bytes .../net8.0-windows/Resources/Images/IMG_WS_185528.png | Bin 0 -> 40901 bytes .../net8.0-windows/Resources/Images/IMG_WS_185535.png | Bin 0 -> 21129 bytes .../net8.0-windows/Resources/Images/IMG_WS_185538.png | Bin 0 -> 32047 bytes .../net8.0-windows/Resources/Images/IMG_WSd_174332.png | Bin 0 -> 30501 bytes .../net8.0-windows/Resources/Images/IMG_WSd_185156.png | Bin 0 -> 30642 bytes .../net8.0-windows/Resources/Images/IMG_WSd_185551.png | Bin 0 -> 28824 bytes .../net8.0-windows/Resources/Images/IMG_WSho_174409.png | Bin 0 -> 29066 bytes .../net8.0-windows/Resources/Images/IMG_WSho_185202.png | Bin 0 -> 24431 bytes .../net8.0-windows/Resources/Images/IMG_WSho_185545.png | Bin 0 -> 20860 bytes .../net8.0-windows/Resources/picture.png | Bin 0 -> 36311 bytes 102 files changed, 2032 insertions(+) create mode 100644 ChudoObuv.slnx create mode 100644 ChudoObuv/App.xaml create mode 100644 ChudoObuv/App.xaml.cs create mode 100644 ChudoObuv/AssemblyInfo.cs create mode 100644 ChudoObuv/ChudoObuv.csproj create mode 100644 ChudoObuv/Data/DataService.cs create mode 100644 ChudoObuv/Data/Database.cs create mode 100644 ChudoObuv/Logic/Messages.cs create mode 100644 ChudoObuv/Logic/PriceCalculator.cs create mode 100644 ChudoObuv/Logic/Session.cs create mode 100644 ChudoObuv/MainWindow.xaml create mode 100644 ChudoObuv/MainWindow.xaml.cs create mode 100644 ChudoObuv/Models/Order.cs create mode 100644 ChudoObuv/Models/OrderItem.cs create mode 100644 ChudoObuv/Models/Product.cs create mode 100644 ChudoObuv/Models/StockItem.cs create mode 100644 ChudoObuv/Models/User.cs create mode 100644 ChudoObuv/Pages/CartPage.xaml create mode 100644 ChudoObuv/Pages/CartPage.xaml.cs create mode 100644 ChudoObuv/Pages/CatalogPage.xaml create mode 100644 ChudoObuv/Pages/CatalogPage.xaml.cs create mode 100644 ChudoObuv/Pages/LoginPage.xaml create mode 100644 ChudoObuv/Pages/LoginPage.xaml.cs create mode 100644 ChudoObuv/Pages/OrderPage.xaml create mode 100644 ChudoObuv/Pages/OrderPage.xaml.cs create mode 100644 ChudoObuv/Pages/OrdersPage.xaml create mode 100644 ChudoObuv/Pages/OrdersPage.xaml.cs create mode 100644 ChudoObuv/Pages/ProductPage.xaml create mode 100644 ChudoObuv/Pages/ProductPage.xaml.cs create mode 100644 ChudoObuv/Resources/Images/IMG_KB_164811.png create mode 100644 ChudoObuv/Resources/Images/IMG_KB_164816.png create mode 100644 ChudoObuv/Resources/Images/IMG_KB_185501.png create mode 100644 ChudoObuv/Resources/Images/IMG_KS_195213.png create mode 100644 ChudoObuv/Resources/Images/IMG_KS_195217.png create mode 100644 ChudoObuv/Resources/Images/IMG_KS_195222.png create mode 100644 ChudoObuv/Resources/Images/IMG_KSd_185508.png create mode 100644 ChudoObuv/Resources/Images/IMG_KSd_195207.png create mode 100644 ChudoObuv/Resources/Images/IMG_KSho_185512.png create mode 100644 ChudoObuv/Resources/Images/IMG_MB_174349.png create mode 100644 ChudoObuv/Resources/Images/IMG_MB_191200.png create mode 100644 ChudoObuv/Resources/Images/IMG_MB_191205.png create mode 100644 ChudoObuv/Resources/Images/IMG_MS_185531.png create mode 100644 ChudoObuv/Resources/Images/IMG_MS_185532.png create mode 100644 ChudoObuv/Resources/Images/IMG_MS_185533.png create mode 100644 ChudoObuv/Resources/Images/IMG_MSho_190514.png create mode 100644 ChudoObuv/Resources/Images/IMG_MSho_190518.png create mode 100644 ChudoObuv/Resources/Images/IMG_MSho_191612.png create mode 100644 ChudoObuv/Resources/Images/IMG_WB_170859.png create mode 100644 ChudoObuv/Resources/Images/IMG_WB_170905.png create mode 100644 ChudoObuv/Resources/Images/IMG_WB_170906.png create mode 100644 ChudoObuv/Resources/Images/IMG_WS_174110.png create mode 100644 ChudoObuv/Resources/Images/IMG_WS_185528.png create mode 100644 ChudoObuv/Resources/Images/IMG_WS_185535.png create mode 100644 ChudoObuv/Resources/Images/IMG_WS_185538.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSd_174332.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSd_185156.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSd_185551.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSho_174409.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSho_185202.png create mode 100644 ChudoObuv/Resources/Images/IMG_WSho_185545.png create mode 100644 ChudoObuv/Resources/app.ico create mode 100644 ChudoObuv/Resources/logo.png create mode 100644 ChudoObuv/Resources/picture.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.deps.json create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.dll create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.exe create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/ChudoObuv.runtimeconfig.json create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Microsoft.Extensions.DependencyInjection.Abstractions.dll create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Microsoft.Extensions.Logging.Abstractions.dll create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/MySqlConnector.dll create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KB_164811.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KB_164816.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KB_185501.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KS_195213.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KS_195217.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KS_195222.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KSd_185508.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KSd_195207.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_KSho_185512.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MB_174349.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MB_191200.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MB_191205.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MS_185531.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MS_185532.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MS_185533.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MSho_190514.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MSho_190518.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_MSho_191612.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WB_170859.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WB_170905.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WB_170906.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WS_174110.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WS_185528.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WS_185535.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WS_185538.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSd_174332.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSd_185156.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSd_185551.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSho_174409.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSho_185202.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/Images/IMG_WSho_185545.png create mode 100644 ChudoObuv/Исполняемые_файлы/net8.0-windows/Resources/picture.png diff --git a/ChudoObuv.slnx b/ChudoObuv.slnx new file mode 100644 index 0000000..63afa36 --- /dev/null +++ b/ChudoObuv.slnx @@ -0,0 +1,3 @@ + + + diff --git a/ChudoObuv/App.xaml b/ChudoObuv/App.xaml new file mode 100644 index 0000000..b7fc699 --- /dev/null +++ b/ChudoObuv/App.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ChudoObuv/App.xaml.cs b/ChudoObuv/App.xaml.cs new file mode 100644 index 0000000..65deaf6 --- /dev/null +++ b/ChudoObuv/App.xaml.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/ChudoObuv/AssemblyInfo.cs b/ChudoObuv/AssemblyInfo.cs new file mode 100644 index 0000000..b0ec827 --- /dev/null +++ b/ChudoObuv/AssemblyInfo.cs @@ -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) +)] diff --git a/ChudoObuv/ChudoObuv.csproj b/ChudoObuv/ChudoObuv.csproj new file mode 100644 index 0000000..35714f5 --- /dev/null +++ b/ChudoObuv/ChudoObuv.csproj @@ -0,0 +1,24 @@ + + + + WinExe + net8.0-windows + disable + enable + true + Resources\app.ico + Чудо Обувь + Чудо Обувь + Чудо Обувь + + + + + + + + + + + + \ No newline at end of file diff --git a/ChudoObuv/Data/DataService.cs b/ChudoObuv/Data/DataService.cs new file mode 100644 index 0000000..785e432 --- /dev/null +++ b/ChudoObuv/Data/DataService.cs @@ -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 +{ + +/// +/// Все запросы приложения к базе данных. +/// +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 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 clients = new List(); + 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 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 products = new List(); + 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 GetCategories() + { + List categories = new List(); + 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; + } + /// + /// Все товарные позиции модели (размерный ряд) с доступным количеством. + /// + public static List 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 items = new List(); + 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 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 orders = new List(); + 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 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 items = new List(); + 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; + } + /// + /// Сохраняет заказ и уменьшает остатки. Возвращает номер заказа. + /// Если какого-то размера уже не хватает — заказ не сохраняется (исключение). + /// + public static int CreateOrder(int clientId, List 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; + } + /// + /// Удаляет заказ и возвращает его товары на склад. + /// + 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(); + } + /// + /// Удаляет товарную позицию из заказа и возвращает ее количество на склад. + /// + 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(); + } + } +} \ No newline at end of file diff --git a/ChudoObuv/Data/Database.cs b/ChudoObuv/Data/Database.cs new file mode 100644 index 0000000..ad82d44 --- /dev/null +++ b/ChudoObuv/Data/Database.cs @@ -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 +{ + /// + /// Подключение к базе данных MySQL. + /// + 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; + } + } +} \ No newline at end of file diff --git a/ChudoObuv/Logic/Messages.cs b/ChudoObuv/Logic/Messages.cs new file mode 100644 index 0000000..a3d1e45 --- /dev/null +++ b/ChudoObuv/Logic/Messages.cs @@ -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 +{ + /// + /// Окна сообщений с заголовком и пиктограммой нужного типа. + /// + 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); + } + /// + /// Подтверждение необратимой операции. Возвращает true, если пользователь нажал «Да». + /// + 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); + } + } +} +} \ No newline at end of file diff --git a/ChudoObuv/Logic/PriceCalculator.cs b/ChudoObuv/Logic/PriceCalculator.cs new file mode 100644 index 0000000..df8f533 --- /dev/null +++ b/ChudoObuv/Logic/PriceCalculator.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ChudoObuv.Logic +{ + /// + /// Алгоритмы расчета показателей товара. + /// + public static class PriceCalculator + { + public const decimal DiscountPercent = 25m; + public const int ManyQuantityThreshold = 5; + public const int LowStockThreshold = 3; + /// + /// Начало предыдущего календарного месяца относительно даты расчета. + /// + 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); + } + /// + /// Цена со скидкой 25%, если в предыдущем календарном месяце заказов товара не было. + /// + public static decimal CalculateDiscountedPrice(decimal price, bool hasOrdersInPreviousMonth) + { + if (hasOrdersInPreviousMonth) + { + return price; + } + return Math.Round(price * (100m - DiscountPercent) / 100m, 2); + } + /// + /// «много», если пар больше пяти, иначе «мало». + /// + public static string GetQuantityLabel(int totalQuantity) + { + return totalQuantity > ManyQuantityThreshold ? "много" : "мало"; + } + public static bool IsLowStock(int totalQuantity) + { + return totalQuantity <= LowStockThreshold; + } + } +} \ No newline at end of file diff --git a/ChudoObuv/Logic/Session.cs b/ChudoObuv/Logic/Session.cs new file mode 100644 index 0000000..e25a9e6 --- /dev/null +++ b/ChudoObuv/Logic/Session.cs @@ -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 +{ + /// + /// Текущий пользователь и формируемый (еще не подтвержденный) заказ. + /// + public static class Session + { + public static User CurrentUser { get; private set; } + /// + /// Клиент, для которого формируется заказ: сам пользователь или клиент, выбранный менеджером. + /// + public static User OrderClient { get; private set; } + public static List Cart { get; } = new List(); + 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; + } + /// + /// Сколько пар данной товарной позиции уже лежит в формируемом заказе. + /// + 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); + } + } + +} +} \ No newline at end of file diff --git a/ChudoObuv/MainWindow.xaml b/ChudoObuv/MainWindow.xaml new file mode 100644 index 0000000..b8f153f --- /dev/null +++ b/ChudoObuv/MainWindow.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + +