Guest

dobegg

Apr 29th, 2026
11
0
Never
Not a member of GistPad yet? Sign Up, it unlocks many cool features!
None 141.44 KB | None | 0 0
  1. ================================================================================
  2. PROJECT FULL SOURCE DUMP — AvaloniaApplication1
  3. В этом файле собран весь исходный код проекта на момент создания.
  4. Не включено: папки bin/, obj/ и прочие артефакты сборки.
  5. ================================================================================
  6.  
  7.  
  8. --------------------------------------------------------------------------------
  9. FILE: Packages.txt
  10. --------------------------------------------------------------------------------
  11. Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent — одна версия.
  12. Npgsql — PostgreSQL.
  13.  
  14. Строка подключения: services/DbService.cs
  15. SQL-скрипт: Database/schema.sql
  16.  
  17.  
  18. --------------------------------------------------------------------------------
  19. FILE: AvaloniaApplication1.sln
  20. --------------------------------------------------------------------------------
  21. Microsoft Visual Studio Solution File, Format Version 12.00
  22. # Visual Studio Version 17
  23. VisualStudioVersion = 17.5.2.0
  24. MinimumVisualStudioVersion = 10.0.40219.1
  25. Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaApplication1", "AvaloniaApplication1.csproj", "{4478C37E-E173-8721-0BB7-2A97BAB6EC35}"
  26. EndProject
  27. Global
  28. GlobalSection(SolutionConfigurationPlatforms) = preSolution
  29. Debug|Any CPU = Debug|Any CPU
  30. Release|Any CPU = Release|Any CPU
  31. EndGlobalSection
  32. GlobalSection(ProjectConfigurationPlatforms) = postSolution
  33. {4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
  34. {4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Debug|Any CPU.Build.0 = Debug|Any CPU
  35. {4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Release|Any CPU.ActiveCfg = Release|Any CPU
  36. {4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Release|Any CPU.Build.0 = Release|Any CPU
  37. EndGlobalSection
  38. GlobalSection(SolutionProperties) = preSolution
  39. HideSolutionNode = FALSE
  40. EndGlobalSection
  41. GlobalSection(ExtensibilityGlobals) = postSolution
  42. SolutionGuid = {0BC0D786-F44D-4044-B8DF-051712874244}
  43. EndGlobalSection
  44. EndGlobal
  45.  
  46.  
  47. --------------------------------------------------------------------------------
  48. FILE: AvaloniaApplication1.csproj
  49. --------------------------------------------------------------------------------
  50. <Project Sdk="Microsoft.NET.Sdk">
  51. <PropertyGroup>
  52. <OutputType>WinExe</OutputType>
  53. <TargetFramework>net8.0</TargetFramework>
  54. <Nullable>enable</Nullable>
  55. <ApplicationManifest>app.manifest</ApplicationManifest>
  56. <RootNamespace>AvaloniaApplication1</RootNamespace>
  57. </PropertyGroup>
  58.  
  59. <ItemGroup>
  60. <PackageReference Include="Avalonia" Version="11.3.11" />
  61. <PackageReference Include="Avalonia.Desktop" Version="11.3.11" />
  62. <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.11" />
  63. <PackageReference Include="Npgsql" Version="8.0.5" />
  64. </ItemGroup>
  65. </Project>
  66.  
  67.  
  68. --------------------------------------------------------------------------------
  69. FILE: app.manifest
  70. --------------------------------------------------------------------------------
  71. <?xml version="1.0" encoding="utf-8"?>
  72. <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  73. <!-- This manifest is used on Windows only.
  74. Don't remove it as it might cause problems with window transparency and embedded controls.
  75. <assemblyIdentity version="1.0.0.0" name="AvaloniaApplication1.Desktop"/>
  76.  
  77. <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  78. <application>
  79. <!-- A list of the Windows versions that this application has been tested on
  80. and is designed to work with. Uncomment the appropriate elements
  81. and Windows will automatically select the most compatible environment. -->
  82.  
  83. <!-- Windows 10 -->
  84. <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  85. </application>
  86. </compatibility>
  87. </assembly>
  88.  
  89.  
  90. --------------------------------------------------------------------------------
  91. FILE: Program.cs
  92. --------------------------------------------------------------------------------
  93. using System;
  94. using Avalonia;
  95.  
  96. namespace AvaloniaApplication1;
  97.  
  98. internal static class Program
  99. {
  100. [STAThread]
  101. public static void Main(string[] args) => BuildAvaloniaApp()
  102. .StartWithClassicDesktopLifetime(args);
  103.  
  104. public static AppBuilder BuildAvaloniaApp()
  105. => AppBuilder.Configure<App>()
  106. .UsePlatformDetect()
  107. .LogToTrace();
  108. }
  109.  
  110.  
  111. --------------------------------------------------------------------------------
  112. FILE: App.axaml
  113. --------------------------------------------------------------------------------
  114. <Application xmlns="https://github.com/avaloniaui";
  115. x:Class="AvaloniaApplication1.App"
  116. RequestedThemeVariant="Default">
  117. <Application.Styles>
  118. <FluentTheme />
  119. </Application.Styles>
  120. </Application>
  121.  
  122.  
  123. --------------------------------------------------------------------------------
  124. FILE: App.axaml.cs
  125. --------------------------------------------------------------------------------
  126. using Avalonia;
  127. using Avalonia.Controls.ApplicationLifetimes;
  128. using Avalonia.Markup.Xaml;
  129. using AvaloniaApplication1.Views;
  130.  
  131. namespace AvaloniaApplication1;
  132.  
  133. public partial class App : Application
  134. {
  135. public override void Initialize()
  136. {
  137. AvaloniaXamlLoader.Load(this);
  138. }
  139.  
  140. public override void OnFrameworkInitializationCompleted()
  141. {
  142. if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
  143. desktop.MainWindow = new LoginWindow();
  144.  
  145. base.OnFrameworkInitializationCompleted();
  146. }
  147. }
  148.  
  149.  
  150. --------------------------------------------------------------------------------
  151. FILE: Models/User.cs
  152. --------------------------------------------------------------------------------
  153. namespace AvaloniaApplication1.Models;
  154.  
  155. public sealed class User
  156. {
  157. public int Id { get; set; }
  158. public string Login { get; set; } = "";
  159. public string Password { get; set; } = "";
  160. public string Role { get; set; } = "";
  161. public string FullName { get; set; } = "";
  162. }
  163.  
  164.  
  165. --------------------------------------------------------------------------------
  166. FILE: Models/Product.cs
  167. --------------------------------------------------------------------------------
  168. namespace AvaloniaApplication1.Models;
  169.  
  170. public sealed class Product
  171. {
  172. public int ProductId { get; set; }
  173. public string Article { get; set; } = "";
  174. public int CategoryId { get; set; }
  175. public int SupplierId { get; set; }
  176. public int TypeProductId { get; set; }
  177. public decimal Price { get; set; }
  178. public string Discount { get; set; } = "0";
  179. public string Description { get; set; } = "";
  180. public string Photo { get; set; } = "";
  181.  
  182. public decimal FinalPrice
  183. {
  184. get
  185. {
  186. if (!decimal.TryParse(Discount.Replace("%", ""), out var discountValue))
  187. {
  188. discountValue = 0;
  189. }
  190.  
  191. return Price * (100m - discountValue) / 100m;
  192. }
  193. }
  194. }
  195.  
  196.  
  197. --------------------------------------------------------------------------------
  198. FILE: Models/Order.cs
  199. --------------------------------------------------------------------------------
  200. namespace AvaloniaApplication1.Models;
  201.  
  202. public sealed class Order
  203. {
  204. public int OrderId { get; set; }
  205. public string OrderArticle { get; set; } = "";
  206. public string DateOrder { get; set; } = "";
  207. public string DateDelivery { get; set; } = "";
  208. public int PickUpPointId { get; set; }
  209. public string Code { get; set; } = "";
  210. public string StatusOrder { get; set; } = "Новый";
  211. public int UserId { get; set; }
  212. public decimal TotalSum { get; set; }
  213. }
  214.  
  215.  
  216. --------------------------------------------------------------------------------
  217. FILE: services/DbService.cs
  218. --------------------------------------------------------------------------------
  219. using System;
  220. using System.Collections.Generic;
  221. using AvaloniaApplication1.Models;
  222. using Npgsql;
  223.  
  224. namespace AvaloniaApplication1.Services;
  225.  
  226. public sealed class DbService
  227. {
  228. private readonly string _connectionString =
  229. "Host=localhost;Port=5432;Username=postgres;Password=postgres;Database=exam_db";
  230.  
  231. public User? Login(string login, string password)
  232. {
  233. using var conn = new NpgsqlConnection(_connectionString);
  234. conn.Open();
  235.  
  236. using var cmd = new NpgsqlCommand(
  237. """
  238. SELECT u.user_id,
  239. u.login,
  240. u.password,
  241. COALESCE(r.name_role, 'client') AS role_name,
  242. TRIM(COALESCE(u.name, '') || ' ' || COALESCE(u.second_name, '') || ' ' || COALESCE(u.patronymic, '')) AS full_name
  243. FROM users u
  244. LEFT JOIN roles r ON r.role_id = u.role_id
  245. WHERE u.login = @l AND u.password = @p
  246. """,
  247. conn);
  248.  
  249. cmd.Parameters.AddWithValue("l", login);
  250. cmd.Parameters.AddWithValue("p", password);
  251.  
  252. using var reader = cmd.ExecuteReader();
  253.  
  254. if (reader.Read())
  255. {
  256. return new User
  257. {
  258. Id = reader.GetInt32(0),
  259. Login = reader.GetString(1),
  260. Password = reader.GetString(2),
  261. Role = NormalizeRole(reader.GetString(3)),
  262. FullName = reader.GetString(4)
  263. };
  264. }
  265.  
  266. return null;
  267. }
  268.  
  269. public List<Product> GetProducts()
  270. {
  271. var list = new List<Product>();
  272.  
  273. using var conn = new NpgsqlConnection(_connectionString);
  274. conn.Open();
  275.  
  276. using var cmd = new NpgsqlCommand(
  277. """
  278. SELECT product_id, article, category_id, supplier_id, type_products_id, price, discount, description, photo
  279. FROM products
  280. ORDER BY product_id
  281. """,
  282. conn);
  283.  
  284. using var reader = cmd.ExecuteReader();
  285.  
  286. while (reader.Read())
  287. {
  288. list.Add(new Product
  289. {
  290. ProductId = reader.GetInt32(0),
  291. Article = reader.GetString(1),
  292. CategoryId = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
  293. SupplierId = reader.IsDBNull(3) ? 0 : reader.GetInt32(3),
  294. TypeProductId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
  295. Price = reader.GetDecimal(5),
  296. Discount = reader.IsDBNull(6) ? "0" : reader.GetString(6),
  297. Description = reader.IsDBNull(7) ? "" : reader.GetString(7),
  298. Photo = reader.IsDBNull(8) ? "" : reader.GetString(8)
  299. });
  300. }
  301.  
  302. return list;
  303. }
  304.  
  305. public void AddProduct(Product product)
  306. {
  307. using var conn = new NpgsqlConnection(_connectionString);
  308. conn.Open();
  309. using var cmd = new NpgsqlCommand(
  310. """
  311. INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo)
  312. VALUES (@article, @categoryId, @supplierId, @typeId, @price, @discount, @description, @photo)
  313. """,
  314. conn);
  315. FillProductParameters(cmd, product);
  316. cmd.ExecuteNonQuery();
  317. }
  318.  
  319. public void UpdateProduct(Product product)
  320. {
  321. using var conn = new NpgsqlConnection(_connectionString);
  322. conn.Open();
  323. using var cmd = new NpgsqlCommand(
  324. """
  325. UPDATE products
  326. SET article = @article,
  327. category_id = @categoryId,
  328. supplier_id = @supplierId,
  329. type_products_id = @typeId,
  330. price = @price,
  331. discount = @discount,
  332. description = @description,
  333. photo = @photo
  334. WHERE product_id = @id
  335. """,
  336. conn);
  337. FillProductParameters(cmd, product);
  338. cmd.Parameters.AddWithValue("id", product.ProductId);
  339. cmd.ExecuteNonQuery();
  340. }
  341.  
  342. public void DeleteProduct(int productId)
  343. {
  344. using var conn = new NpgsqlConnection(_connectionString);
  345. conn.Open();
  346. using var cmd = new NpgsqlCommand("DELETE FROM products WHERE product_id = @id", conn);
  347. cmd.Parameters.AddWithValue("id", productId);
  348. cmd.ExecuteNonQuery();
  349. }
  350.  
  351. public List<Order> GetOrders()
  352. {
  353. var list = new List<Order>();
  354. using var conn = new NpgsqlConnection(_connectionString);
  355. conn.Open();
  356. using var cmd = new NpgsqlCommand(
  357. """
  358. SELECT order_id, order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum
  359. FROM orders
  360. ORDER BY order_id
  361. """,
  362. conn);
  363. using var reader = cmd.ExecuteReader();
  364. while (reader.Read())
  365. {
  366. list.Add(new Order
  367. {
  368. OrderId = reader.GetInt32(0),
  369. OrderArticle = reader.IsDBNull(1) ? "" : reader.GetString(1),
  370. DateOrder = reader.IsDBNull(2) ? "" : reader.GetDateTime(2).ToString("yyyy-MM-dd"),
  371. DateDelivery = reader.IsDBNull(3) ? "" : reader.GetDateTime(3).ToString("yyyy-MM-dd"),
  372. PickUpPointId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
  373. Code = reader.IsDBNull(5) ? "" : reader.GetString(5),
  374. StatusOrder = reader.IsDBNull(6) ? "Новый" : reader.GetString(6),
  375. UserId = reader.IsDBNull(7) ? 0 : reader.GetInt32(7),
  376. TotalSum = reader.IsDBNull(8) ? 0m : reader.GetDecimal(8)
  377. });
  378. }
  379.  
  380. return list;
  381. }
  382.  
  383. public void AddOrder(Order order)
  384. {
  385. using var conn = new NpgsqlConnection(_connectionString);
  386. conn.Open();
  387. using var cmd = new NpgsqlCommand(
  388. """
  389. INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum)
  390. VALUES (@article, @dateOrder, @dateDelivery, @pickUpPointId, @code, @status, @userId, @totalSum)
  391. """,
  392. conn);
  393. FillOrderParameters(cmd, order);
  394. cmd.ExecuteNonQuery();
  395. }
  396.  
  397. public void UpdateOrder(Order order)
  398. {
  399. using var conn = new NpgsqlConnection(_connectionString);
  400. conn.Open();
  401. using var cmd = new NpgsqlCommand(
  402. """
  403. UPDATE orders
  404. SET order_article = @article,
  405. date_order = @dateOrder,
  406. date_delivery = @dateDelivery,
  407. pick_up_point_id = @pickUpPointId,
  408. code = @code,
  409. status_order = @status,
  410. user_id = @userId,
  411. total_sum = @totalSum
  412. WHERE order_id = @id
  413. """,
  414. conn);
  415. FillOrderParameters(cmd, order);
  416. cmd.Parameters.AddWithValue("id", order.OrderId);
  417. cmd.ExecuteNonQuery();
  418. }
  419.  
  420. public void DeleteOrder(int orderId)
  421. {
  422. using var conn = new NpgsqlConnection(_connectionString);
  423. conn.Open();
  424. using var cmd = new NpgsqlCommand("DELETE FROM orders WHERE order_id = @id", conn);
  425. cmd.Parameters.AddWithValue("id", orderId);
  426. cmd.ExecuteNonQuery();
  427. }
  428.  
  429. private static void FillProductParameters(NpgsqlCommand cmd, Product product)
  430. {
  431. cmd.Parameters.AddWithValue("article", product.Article);
  432. cmd.Parameters.AddWithValue("categoryId", product.CategoryId == 0 ? DBNull.Value : product.CategoryId);
  433. cmd.Parameters.AddWithValue("supplierId", product.SupplierId == 0 ? DBNull.Value : product.SupplierId);
  434. cmd.Parameters.AddWithValue("typeId", product.TypeProductId == 0 ? DBNull.Value : product.TypeProductId);
  435. cmd.Parameters.AddWithValue("price", product.Price);
  436. cmd.Parameters.AddWithValue("discount", product.Discount);
  437. cmd.Parameters.AddWithValue("description", string.IsNullOrWhiteSpace(product.Description) ? DBNull.Value : product.Description);
  438. cmd.Parameters.AddWithValue("photo", string.IsNullOrWhiteSpace(product.Photo) ? DBNull.Value : product.Photo);
  439. }
  440.  
  441. private static void FillOrderParameters(NpgsqlCommand cmd, Order order)
  442. {
  443. cmd.Parameters.AddWithValue("article", string.IsNullOrWhiteSpace(order.OrderArticle) ? DBNull.Value : order.OrderArticle);
  444. cmd.Parameters.AddWithValue("dateOrder", ParseDateOrDbNull(order.DateOrder));
  445. cmd.Parameters.AddWithValue("dateDelivery", ParseDateOrDbNull(order.DateDelivery));
  446. cmd.Parameters.AddWithValue("pickUpPointId", order.PickUpPointId == 0 ? DBNull.Value : order.PickUpPointId);
  447. cmd.Parameters.AddWithValue("code", string.IsNullOrWhiteSpace(order.Code) ? DBNull.Value : order.Code);
  448. cmd.Parameters.AddWithValue("status", string.IsNullOrWhiteSpace(order.StatusOrder) ? "Новый" : order.StatusOrder);
  449. cmd.Parameters.AddWithValue("userId", order.UserId == 0 ? DBNull.Value : order.UserId);
  450. cmd.Parameters.AddWithValue("totalSum", order.TotalSum);
  451. }
  452.  
  453. private static object ParseDateOrDbNull(string date)
  454. {
  455. return DateTime.TryParse(date, out var value) ? value : DBNull.Value;
  456. }
  457.  
  458. private static string NormalizeRole(string role)
  459. {
  460. var value = role.Trim().ToLowerInvariant();
  461. return value switch
  462. {
  463. "админ" => "admin",
  464. "администратор" => "admin",
  465. "manager" => "manager",
  466. "менеджер" => "manager",
  467. "client" => "client",
  468. "клиент" => "client",
  469. _ => value
  470. };
  471. }
  472. }
  473.  
  474.  
  475. --------------------------------------------------------------------------------
  476. FILE: services/bdService.cs
  477. --------------------------------------------------------------------------------
  478. using System.Collections.Generic;
  479. using AvaloniaApplication1.Models;
  480. using Npgsql;
  481.  
  482. namespace AvaloniaApplication.service
  483. {
  484.  
  485. }
  486.  
  487.  
  488. --------------------------------------------------------------------------------
  489. FILE: Views/LoginWindow.axaml
  490. --------------------------------------------------------------------------------
  491. x:Class="AvaloniaApplication1.Views.LoginWindow"
  492. Width="420" Height="300"
  493. Title="Авторизация">
  494.  
  495. <StackPanel Margin="20" Spacing="12">
  496.  
  497. <TextBlock Text="Вход в систему"
  498. FontSize="20"
  499. FontWeight="Bold"
  500. HorizontalAlignment="Center"/>
  501.  
  502. <TextBox x:Name="loginBox"
  503. Watermark="Логин"/>
  504.  
  505. <TextBox x:Name="passwordBox"
  506. PasswordChar="*"
  507. Watermark="Пароль"/>
  508.  
  509. <TextBlock x:Name="errorText"
  510. Foreground="Red"
  511. TextWrapping="Wrap"/>
  512.  
  513. <Button Content="Войти"
  514. Click="Login_Click"
  515. HorizontalAlignment="Stretch"/>
  516.  
  517. <Button Content="Войти как гость"
  518. Click="Guest_Click"
  519. HorizontalAlignment="Stretch"/>
  520.  
  521. </StackPanel>
  522.  
  523. </Window>
  524.  
  525.  
  526. --------------------------------------------------------------------------------
  527. FILE: Views/LoginWindow.axaml.cs
  528. --------------------------------------------------------------------------------
  529. using System;
  530. using Avalonia;
  531. using Avalonia.Controls;
  532. using Avalonia.Controls.ApplicationLifetimes;
  533. using Avalonia.Interactivity;
  534. using AvaloniaApplication1.Models;
  535. using AvaloniaApplication1.Services;
  536.  
  537. namespace AvaloniaApplication1.Views;
  538.  
  539. public partial class LoginWindow : Window
  540. {
  541. private readonly DbService _dbService = new();
  542.  
  543. public LoginWindow()
  544. {
  545. InitializeComponent();
  546. }
  547.  
  548. private void Login_Click(object? sender, RoutedEventArgs e)
  549. {
  550. errorText.Text = string.Empty;
  551.  
  552. var login = loginBox.Text?.Trim() ?? string.Empty;
  553. var password = passwordBox.Text?.Trim() ?? string.Empty;
  554.  
  555. if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
  556. {
  557. errorText.Text = "Введите логин и пароль.";
  558. return;
  559. }
  560.  
  561. try
  562. {
  563. var user = _dbService.Login(login, password);
  564. if (user is null)
  565. {
  566. errorText.Text = "Неверный логин или пароль.";
  567. return;
  568. }
  569.  
  570. OpenRoleMenu(user);
  571. }
  572. catch (Exception ex)
  573. {
  574. errorText.Text = $"Ошибка подключения к БД: {ex.Message}";
  575. }
  576. }
  577.  
  578. private void Guest_Click(object? sender, RoutedEventArgs e)
  579. {
  580. errorText.Text = string.Empty;
  581. OpenRoleMenu(new User
  582. {
  583. Login = "guest",
  584. FullName = "Гость",
  585. Role = "client"
  586. });
  587. }
  588.  
  589. private void OpenRoleMenu(User user)
  590. {
  591. var roleWindow = new RoleWindow(user, _dbService);
  592. if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
  593. {
  594. desktop.MainWindow = roleWindow;
  595. }
  596.  
  597. roleWindow.Show();
  598. Close();
  599. }
  600. }
  601.  
  602.  
  603. --------------------------------------------------------------------------------
  604. FILE: Views/RoleWindow.axaml
  605. --------------------------------------------------------------------------------
  606. x:Class="AvaloniaApplication1.Views.RoleWindow"
  607. Width="450" Height="260"
  608. Title="Главное меню">
  609. <StackPanel Margin="20" Spacing="12">
  610. <TextBlock x:Name="helloText"
  611. FontSize="18"
  612. FontWeight="Bold"/>
  613.  
  614. <Button Content="Просмотр товаров"
  615. Click="OpenProducts_Click"
  616. HorizontalAlignment="Stretch"/>
  617.  
  618. <Button x:Name="ordersButton"
  619. Content="Просмотр заказов"
  620. Click="OpenOrders_Click"
  621. HorizontalAlignment="Stretch"
  622. IsVisible="False"/>
  623.  
  624. <Button Content="Выход"
  625. Click="Exit_Click"
  626. HorizontalAlignment="Stretch"/>
  627. </StackPanel>
  628. </Window>
  629.  
  630.  
  631. --------------------------------------------------------------------------------
  632. FILE: Views/RoleWindow.axaml.cs
  633. --------------------------------------------------------------------------------
  634. using Avalonia;
  635. using Avalonia.Controls;
  636. using Avalonia.Controls.ApplicationLifetimes;
  637. using Avalonia.Interactivity;
  638. using AvaloniaApplication1.Models;
  639. using AvaloniaApplication1.Services;
  640.  
  641. namespace AvaloniaApplication1.Views;
  642.  
  643. public partial class RoleWindow : Window
  644. {
  645. private readonly User _user;
  646. private readonly DbService _dbService;
  647.  
  648. public RoleWindow(User user, DbService dbService)
  649. {
  650. _user = user;
  651. _dbService = dbService;
  652. InitializeComponent();
  653.  
  654. helloText.Text = $"Пользователь: {(_user.FullName.Trim() == string.Empty ? _user.Login : _user.FullName)}";
  655. ordersButton.IsVisible = IsManagerOrAdmin(_user.Role);
  656. }
  657.  
  658. private async void OpenProducts_Click(object? sender, RoutedEventArgs e)
  659. {
  660. await new ProductsWindow(_user, _dbService).ShowDialog(this);
  661. }
  662.  
  663. private async void OpenOrders_Click(object? sender, RoutedEventArgs e)
  664. {
  665. await new OrdersWindow(_user, _dbService).ShowDialog(this);
  666. }
  667.  
  668. private void Exit_Click(object? sender, RoutedEventArgs e)
  669. {
  670. var login = new LoginWindow();
  671. if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
  672. {
  673. desktop.MainWindow = login;
  674. }
  675.  
  676. login.Show();
  677. Close();
  678. }
  679.  
  680. private static bool IsManagerOrAdmin(string role)
  681. {
  682. return role is "manager" or "admin";
  683. }
  684. }
  685.  
  686.  
  687. --------------------------------------------------------------------------------
  688. FILE: Views/ProductsWindow.axaml
  689. --------------------------------------------------------------------------------
  690. x:Class="AvaloniaApplication1.Views.ProductsWindow"
  691. Width="1000" Height="650"
  692. Title="Товары">
  693. <Grid RowDefinitions="Auto,*,Auto,Auto" Margin="12">
  694. <TextBlock x:Name="titleText"
  695. FontSize="18"
  696. FontWeight="Bold"
  697. Margin="0,0,0,10"/>
  698.  
  699. <ScrollViewer Grid.Row="1">
  700. <ItemsControl x:Name="productsItemsControl">
  701. <ItemsControl.ItemsPanel>
  702. <ItemsPanelTemplate>
  703. <WrapPanel Orientation="Horizontal"/>
  704. </ItemsPanelTemplate>
  705. </ItemsControl.ItemsPanel>
  706. <ItemsControl.ItemTemplate>
  707. <DataTemplate>
  708. <Border BorderBrush="#CCCCCC"
  709. BorderThickness="1"
  710. CornerRadius="6"
  711. Width="240"
  712. Margin="6"
  713. Padding="10">
  714. <StackPanel Spacing="6">
  715. <TextBlock Text="{Binding ProductId, StringFormat='ID: {0}'}" FontWeight="SemiBold"/>
  716. <TextBlock Text="{Binding Article, StringFormat='Артикул: {0}'}"/>
  717. <TextBlock Text="{Binding Description}" TextWrapping="Wrap"/>
  718. <TextBlock Text="{Binding Price, StringFormat='Цена: {0:F2}'}"/>
  719. <TextBlock Text="{Binding Discount, StringFormat='Скидка: {0}%'}"/>
  720. <TextBlock Text="{Binding FinalPrice, StringFormat='Итог: {0:F2}'}" FontWeight="Bold"/>
  721. </StackPanel>
  722. </Border>
  723. </DataTemplate>
  724. </ItemsControl.ItemTemplate>
  725. </ItemsControl>
  726. </ScrollViewer>
  727.  
  728. <StackPanel Grid.Row="2" x:Name="managePanel" Orientation="Horizontal" Spacing="8" Margin="0,10,0,10" IsVisible="False">
  729. <Button Content="Добавить товар" Click="AddProduct_Click"/>
  730. <Button Content="Изменить выбранный товар" Click="EditProduct_Click"/>
  731. <Button Content="Удалить выбранный товар" Click="DeleteProduct_Click"/>
  732. </StackPanel>
  733.  
  734. <ComboBox Grid.Row="3"
  735. x:Name="productsComboBox"
  736. HorizontalAlignment="Stretch"
  737. DisplayMemberBinding="{Binding Article}"/>
  738. </Grid>
  739. </Window>
  740.  
  741.  
  742. --------------------------------------------------------------------------------
  743. FILE: Views/ProductsWindow.axaml.cs
  744. --------------------------------------------------------------------------------
  745. using System;
  746. using System.Collections.Generic;
  747. using Avalonia.Controls;
  748. using Avalonia.Interactivity;
  749. using AvaloniaApplication1.Models;
  750. using AvaloniaApplication1.Services;
  751.  
  752. namespace AvaloniaApplication1.Views;
  753.  
  754. public partial class ProductsWindow : Window
  755. {
  756. private readonly User _user;
  757. private readonly DbService _dbService;
  758. private List<Product> _products = [];
  759.  
  760. public ProductsWindow(User user, DbService dbService)
  761. {
  762. _user = user;
  763. _dbService = dbService;
  764. InitializeComponent();
  765.  
  766. titleText.Text = $"Товары ({RoleTitle(_user.Role)})";
  767. managePanel.IsVisible = IsManagerOrAdmin(_user.Role);
  768. LoadProducts();
  769. }
  770.  
  771. private void LoadProducts()
  772. {
  773. try
  774. {
  775. _products = _dbService.GetProducts();
  776. productsItemsControl.ItemsSource = _products;
  777. productsComboBox.ItemsSource = _products;
  778. if (_products.Count > 0)
  779. {
  780. productsComboBox.SelectedIndex = 0;
  781. }
  782. }
  783. catch (Exception ex)
  784. {
  785. _ = ShowError(ex.Message);
  786. }
  787. }
  788.  
  789. private async void AddProduct_Click(object? sender, RoutedEventArgs e)
  790. {
  791. var editWindow = new ProductEditWindow(new Product());
  792. var result = await editWindow.ShowDialog<Product?>(this);
  793. if (result is null)
  794. {
  795. return;
  796. }
  797.  
  798. try
  799. {
  800. _dbService.AddProduct(result);
  801. LoadProducts();
  802. }
  803. catch (Exception ex)
  804. {
  805. _ = ShowError(ex.Message);
  806. }
  807. }
  808.  
  809. private async void EditProduct_Click(object? sender, RoutedEventArgs e)
  810. {
  811. if (productsComboBox.SelectedItem is not Product selected)
  812. {
  813. return;
  814. }
  815.  
  816. var clone = new Product
  817. {
  818. ProductId = selected.ProductId,
  819. Article = selected.Article,
  820. CategoryId = selected.CategoryId,
  821. SupplierId = selected.SupplierId,
  822. TypeProductId = selected.TypeProductId,
  823. Price = selected.Price,
  824. Discount = selected.Discount,
  825. Description = selected.Description,
  826. Photo = selected.Photo
  827. };
  828.  
  829. var editWindow = new ProductEditWindow(clone);
  830. var result = await editWindow.ShowDialog<Product?>(this);
  831. if (result is null)
  832. {
  833. return;
  834. }
  835.  
  836. try
  837. {
  838. _dbService.UpdateProduct(result);
  839. LoadProducts();
  840. }
  841. catch (Exception ex)
  842. {
  843. await ShowError(ex.Message);
  844. }
  845. }
  846.  
  847. private async void DeleteProduct_Click(object? sender, RoutedEventArgs e)
  848. {
  849. if (productsComboBox.SelectedItem is not Product selected)
  850. {
  851. return;
  852. }
  853.  
  854. try
  855. {
  856. _dbService.DeleteProduct(selected.ProductId);
  857. LoadProducts();
  858. }
  859. catch (Exception ex)
  860. {
  861. await ShowError(ex.Message);
  862. }
  863. }
  864.  
  865. private async System.Threading.Tasks.Task ShowError(string message)
  866. {
  867. var dialog = new Window
  868. {
  869. Width = 420,
  870. Height = 180,
  871. Title = "Ошибка"
  872. };
  873. dialog.Content = new StackPanel
  874. {
  875. Margin = new Avalonia.Thickness(16),
  876. Spacing = 12,
  877. Children =
  878. {
  879. new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
  880. new Button
  881. {
  882. Content = "OK",
  883. HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
  884. }
  885. }
  886. };
  887. ((Button)((StackPanel)dialog.Content).Children[1]).Click += (_, _) => dialog.Close();
  888. await dialog.ShowDialog(this);
  889. }
  890.  
  891. private static bool IsManagerOrAdmin(string role) => role is "manager" or "admin";
  892.  
  893. private static string RoleTitle(string role) => role switch
  894. {
  895. "manager" => "менеджер",
  896. "admin" => "админ",
  897. _ => "клиент/гость"
  898. };
  899. }
  900.  
  901.  
  902. --------------------------------------------------------------------------------
  903. FILE: Views/ProductEditWindow.axaml
  904. --------------------------------------------------------------------------------
  905. x:Class="AvaloniaApplication1.Views.ProductEditWindow"
  906. Width="460" Height="520"
  907. Title="Редактор товара">
  908. <StackPanel Margin="16" Spacing="8">
  909. <TextBlock Text="Артикул"/>
  910. <TextBox x:Name="articleBox"/>
  911. <TextBlock Text="Категория ID"/>
  912. <TextBox x:Name="categoryBox"/>
  913. <TextBlock Text="Поставщик ID"/>
  914. <TextBox x:Name="supplierBox"/>
  915. <TextBlock Text="Тип товара ID"/>
  916. <TextBox x:Name="typeBox"/>
  917. <TextBlock Text="Цена"/>
  918. <TextBox x:Name="priceBox"/>
  919. <TextBlock Text="Скидка (число, например 15)"/>
  920. <TextBox x:Name="discountBox"/>
  921. <TextBlock Text="Описание"/>
  922. <TextBox x:Name="descriptionBox" AcceptsReturn="True" Height="70" TextWrapping="Wrap"/>
  923. <TextBlock Text="Фото (путь)"/>
  924. <TextBox x:Name="photoBox"/>
  925.  
  926. <StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
  927. <Button Content="Сохранить" Click="Save_Click"/>
  928. <Button Content="Отмена" Click="Cancel_Click"/>
  929. </StackPanel>
  930. </StackPanel>
  931. </Window>
  932.  
  933.  
  934. --------------------------------------------------------------------------------
  935. FILE: Views/ProductEditWindow.axaml.cs
  936. --------------------------------------------------------------------------------
  937. using System.Globalization;
  938. using Avalonia.Controls;
  939. using Avalonia.Interactivity;
  940. using AvaloniaApplication1.Models;
  941.  
  942. namespace AvaloniaApplication1.Views;
  943.  
  944. public partial class ProductEditWindow : Window
  945. {
  946. private readonly Product _source;
  947.  
  948. public ProductEditWindow(Product product)
  949. {
  950. _source = product;
  951. InitializeComponent();
  952. Fill();
  953. }
  954.  
  955. private void Fill()
  956. {
  957. articleBox.Text = _source.Article;
  958. categoryBox.Text = _source.CategoryId.ToString();
  959. supplierBox.Text = _source.SupplierId.ToString();
  960. typeBox.Text = _source.TypeProductId.ToString();
  961. priceBox.Text = _source.Price.ToString(CultureInfo.InvariantCulture);
  962. discountBox.Text = _source.Discount;
  963. descriptionBox.Text = _source.Description;
  964. photoBox.Text = _source.Photo;
  965. }
  966.  
  967. private void Save_Click(object? sender, RoutedEventArgs e)
  968. {
  969. if (string.IsNullOrWhiteSpace(articleBox.Text))
  970. {
  971. return;
  972. }
  973.  
  974. if (!decimal.TryParse(priceBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var price))
  975. {
  976. return;
  977. }
  978.  
  979. var result = new Product
  980. {
  981. ProductId = _source.ProductId,
  982. Article = articleBox.Text.Trim(),
  983. CategoryId = ParseInt(categoryBox.Text),
  984. SupplierId = ParseInt(supplierBox.Text),
  985. TypeProductId = ParseInt(typeBox.Text),
  986. Price = price,
  987. Discount = string.IsNullOrWhiteSpace(discountBox.Text) ? "0" : discountBox.Text.Trim(),
  988. Description = descriptionBox.Text?.Trim() ?? "",
  989. Photo = photoBox.Text?.Trim() ?? ""
  990. };
  991. Close(result);
  992. }
  993.  
  994. private void Cancel_Click(object? sender, RoutedEventArgs e)
  995. {
  996. Close(null);
  997. }
  998.  
  999. private static int ParseInt(string? value)
  1000. {
  1001. return int.TryParse(value, out var parsed) ? parsed : 0;
  1002. }
  1003. }
  1004.  
  1005.  
  1006. --------------------------------------------------------------------------------
  1007. FILE: Views/OrdersWindow.axaml
  1008. --------------------------------------------------------------------------------
  1009. x:Class="AvaloniaApplication1.Views.OrdersWindow"
  1010. Width="980" Height="620"
  1011. Title="Заказы">
  1012. <Grid RowDefinitions="Auto,*,Auto" Margin="12">
  1013. <TextBlock x:Name="titleText" FontSize="18" FontWeight="Bold" Margin="0,0,0,10"/>
  1014.  
  1015. <ListBox Grid.Row="1" x:Name="ordersListBox">
  1016. <ListBox.ItemTemplate>
  1017. <DataTemplate>
  1018. <Border BorderBrush="#CCCCCC" BorderThickness="1" CornerRadius="6" Padding="8" Margin="0,0,0,8">
  1019. <StackPanel Spacing="4">
  1020. <TextBlock Text="{Binding OrderId, StringFormat='ID: {0}'}" FontWeight="Bold"/>
  1021. <TextBlock Text="{Binding OrderArticle, StringFormat='Артикул: {0}'}"/>
  1022. <TextBlock Text="{Binding StatusOrder, StringFormat='Статус: {0}'}"/>
  1023. <TextBlock Text="{Binding TotalSum, StringFormat='Сумма: {0:F2}'}"/>
  1024. <TextBlock Text="{Binding DateOrder, StringFormat='Дата заказа: {0}'}"/>
  1025. <TextBlock Text="{Binding DateDelivery, StringFormat='Дата доставки: {0}'}"/>
  1026. </StackPanel>
  1027. </Border>
  1028. </DataTemplate>
  1029. </ListBox.ItemTemplate>
  1030. </ListBox>
  1031.  
  1032. <StackPanel Grid.Row="2" x:Name="adminActions" Orientation="Horizontal" Spacing="8" Margin="0,10,0,0" IsVisible="False">
  1033. <Button Content="Добавить заказ" Click="AddOrder_Click"/>
  1034. <Button Content="Изменить выбранный заказ" Click="EditOrder_Click"/>
  1035. <Button Content="Удалить выбранный заказ" Click="DeleteOrder_Click"/>
  1036. </StackPanel>
  1037. </Grid>
  1038. </Window>
  1039.  
  1040.  
  1041. --------------------------------------------------------------------------------
  1042. FILE: Views/OrdersWindow.axaml.cs
  1043. --------------------------------------------------------------------------------
  1044. using System;
  1045. using System.Collections.Generic;
  1046. using Avalonia.Controls;
  1047. using Avalonia.Interactivity;
  1048. using AvaloniaApplication1.Models;
  1049. using AvaloniaApplication1.Services;
  1050.  
  1051. namespace AvaloniaApplication1.Views;
  1052.  
  1053. public partial class OrdersWindow : Window
  1054. {
  1055. private readonly User _user;
  1056. private readonly DbService _dbService;
  1057. private List<Order> _orders = [];
  1058.  
  1059. public OrdersWindow(User user, DbService dbService)
  1060. {
  1061. _user = user;
  1062. _dbService = dbService;
  1063. InitializeComponent();
  1064.  
  1065. titleText.Text = "Панель заказов";
  1066. adminActions.IsVisible = _user.Role == "admin";
  1067. LoadOrders();
  1068. }
  1069.  
  1070. private void LoadOrders()
  1071. {
  1072. try
  1073. {
  1074. _orders = _dbService.GetOrders();
  1075. ordersListBox.ItemsSource = _orders;
  1076. }
  1077. catch (Exception ex)
  1078. {
  1079. _ = ShowError(ex.Message);
  1080. }
  1081. }
  1082.  
  1083. private async void AddOrder_Click(object? sender, RoutedEventArgs e)
  1084. {
  1085. var editor = new OrderEditWindow(new Order());
  1086. var result = await editor.ShowDialog<Order?>(this);
  1087. if (result is null)
  1088. {
  1089. return;
  1090. }
  1091.  
  1092. try
  1093. {
  1094. _dbService.AddOrder(result);
  1095. LoadOrders();
  1096. }
  1097. catch (Exception ex)
  1098. {
  1099. await ShowError(ex.Message);
  1100. }
  1101. }
  1102.  
  1103. private async void EditOrder_Click(object? sender, RoutedEventArgs e)
  1104. {
  1105. if (ordersListBox.SelectedItem is not Order selected)
  1106. {
  1107. return;
  1108. }
  1109.  
  1110. var editor = new OrderEditWindow(new Order
  1111. {
  1112. OrderId = selected.OrderId,
  1113. OrderArticle = selected.OrderArticle,
  1114. DateOrder = selected.DateOrder,
  1115. DateDelivery = selected.DateDelivery,
  1116. PickUpPointId = selected.PickUpPointId,
  1117. Code = selected.Code,
  1118. StatusOrder = selected.StatusOrder,
  1119. UserId = selected.UserId,
  1120. TotalSum = selected.TotalSum
  1121. });
  1122. var result = await editor.ShowDialog<Order?>(this);
  1123. if (result is null)
  1124. {
  1125. return;
  1126. }
  1127.  
  1128. try
  1129. {
  1130. _dbService.UpdateOrder(result);
  1131. LoadOrders();
  1132. }
  1133. catch (Exception ex)
  1134. {
  1135. await ShowError(ex.Message);
  1136. }
  1137. }
  1138.  
  1139. private async void DeleteOrder_Click(object? sender, RoutedEventArgs e)
  1140. {
  1141. if (ordersListBox.SelectedItem is not Order selected)
  1142. {
  1143. return;
  1144. }
  1145.  
  1146. try
  1147. {
  1148. _dbService.DeleteOrder(selected.OrderId);
  1149. LoadOrders();
  1150. }
  1151. catch (Exception ex)
  1152. {
  1153. await ShowError(ex.Message);
  1154. }
  1155. }
  1156.  
  1157. private async System.Threading.Tasks.Task ShowError(string message)
  1158. {
  1159. var dialog = new Window
  1160. {
  1161. Width = 420,
  1162. Height = 180,
  1163. Title = "Ошибка"
  1164. };
  1165. dialog.Content = new StackPanel
  1166. {
  1167. Margin = new Avalonia.Thickness(16),
  1168. Spacing = 12,
  1169. Children =
  1170. {
  1171. new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
  1172. new Button
  1173. {
  1174. Content = "OK",
  1175. HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
  1176. }
  1177. }
  1178. };
  1179. ((Button)((StackPanel)dialog.Content).Children[1]).Click += (_, _) => dialog.Close();
  1180. await dialog.ShowDialog(this);
  1181. }
  1182. }
  1183.  
  1184.  
  1185. --------------------------------------------------------------------------------
  1186. FILE: Views/OrderEditWindow.axaml
  1187. --------------------------------------------------------------------------------
  1188. x:Class="AvaloniaApplication1.Views.OrderEditWindow"
  1189. Width="460" Height="520"
  1190. Title="Редактор заказа">
  1191. <StackPanel Margin="16" Spacing="8">
  1192. <TextBlock Text="Артикул заказа"/>
  1193. <TextBox x:Name="articleBox"/>
  1194. <TextBlock Text="Дата заказа (yyyy-MM-dd)"/>
  1195. <TextBox x:Name="dateOrderBox"/>
  1196. <TextBlock Text="Дата доставки (yyyy-MM-dd)"/>
  1197. <TextBox x:Name="dateDeliveryBox"/>
  1198. <TextBlock Text="ID пункта выдачи"/>
  1199. <TextBox x:Name="pickUpBox"/>
  1200. <TextBlock Text="Код"/>
  1201. <TextBox x:Name="codeBox"/>
  1202. <TextBlock Text="Статус"/>
  1203. <TextBox x:Name="statusBox"/>
  1204. <TextBlock Text="ID пользователя"/>
  1205. <TextBox x:Name="userBox"/>
  1206. <TextBlock Text="Сумма"/>
  1207. <TextBox x:Name="sumBox"/>
  1208.  
  1209. <StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
  1210. <Button Content="Сохранить" Click="Save_Click"/>
  1211. <Button Content="Отмена" Click="Cancel_Click"/>
  1212. </StackPanel>
  1213. </StackPanel>
  1214. </Window>
  1215.  
  1216.  
  1217. --------------------------------------------------------------------------------
  1218. FILE: Views/OrderEditWindow.axaml.cs
  1219. --------------------------------------------------------------------------------
  1220. using System.Globalization;
  1221. using Avalonia.Controls;
  1222. using Avalonia.Interactivity;
  1223. using AvaloniaApplication1.Models;
  1224.  
  1225. namespace AvaloniaApplication1.Views;
  1226.  
  1227. public partial class OrderEditWindow : Window
  1228. {
  1229. private readonly Order _source;
  1230.  
  1231. public OrderEditWindow(Order order)
  1232. {
  1233. _source = order;
  1234. InitializeComponent();
  1235. Fill();
  1236. }
  1237.  
  1238. private void Fill()
  1239. {
  1240. articleBox.Text = _source.OrderArticle;
  1241. dateOrderBox.Text = _source.DateOrder;
  1242. dateDeliveryBox.Text = _source.DateDelivery;
  1243. pickUpBox.Text = _source.PickUpPointId.ToString();
  1244. codeBox.Text = _source.Code;
  1245. statusBox.Text = _source.StatusOrder;
  1246. userBox.Text = _source.UserId.ToString();
  1247. sumBox.Text = _source.TotalSum.ToString(CultureInfo.InvariantCulture);
  1248. }
  1249.  
  1250. private void Save_Click(object? sender, RoutedEventArgs e)
  1251. {
  1252. if (!decimal.TryParse(sumBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var sum))
  1253. {
  1254. return;
  1255. }
  1256.  
  1257. var result = new Order
  1258. {
  1259. OrderId = _source.OrderId,
  1260. OrderArticle = articleBox.Text?.Trim() ?? "",
  1261. DateOrder = dateOrderBox.Text?.Trim() ?? "",
  1262. DateDelivery = dateDeliveryBox.Text?.Trim() ?? "",
  1263. PickUpPointId = ParseInt(pickUpBox.Text),
  1264. Code = codeBox.Text?.Trim() ?? "",
  1265. StatusOrder = string.IsNullOrWhiteSpace(statusBox.Text) ? "Новый" : statusBox.Text.Trim(),
  1266. UserId = ParseInt(userBox.Text),
  1267. TotalSum = sum
  1268. };
  1269. Close(result);
  1270. }
  1271.  
  1272. private void Cancel_Click(object? sender, RoutedEventArgs e)
  1273. {
  1274. Close(null);
  1275. }
  1276.  
  1277. private static int ParseInt(string? value)
  1278. {
  1279. return int.TryParse(value, out var parsed) ? parsed : 0;
  1280. }
  1281. }
  1282.  
  1283. ================================================================================
  1284. ОБНОВЛЕНИЕ: АКТУАЛЬНЫЙ DbService.cs
  1285. ================================================================================
  1286.  
  1287. --------------------------------------------------------------------------------
  1288. FILE: services/DbService.cs (UPDATED)
  1289. --------------------------------------------------------------------------------
  1290. using System;
  1291. using System.Collections.Generic;
  1292. using AvaloniaApplication1.Models;
  1293. using Npgsql;
  1294.  
  1295. namespace AvaloniaApplication1.Services;
  1296.  
  1297. public sealed class DbService
  1298. {
  1299. private readonly string[] _connectionCandidates =
  1300. [
  1301. "Host=localhost;Port=5432;Username=postgres;Password=123;Database=321"
  1302. ];
  1303.  
  1304. public User? Login(string login, string password)
  1305. {
  1306. using var conn = OpenConnection();
  1307.  
  1308. using var cmd = new NpgsqlCommand(
  1309. """
  1310. SELECT u.user_id,
  1311. u.login,
  1312. u.password,
  1313. COALESCE(r.name_role, 'client') AS role_name,
  1314. TRIM(COALESCE(u.name, '') || ' ' || COALESCE(u.second_name, '') || ' ' || COALESCE(u.patronymic, '')) AS full_name
  1315. FROM users u
  1316. LEFT JOIN roles r ON r.role_id = u.role_id
  1317. WHERE u.login = @l AND u.password = @p
  1318. """,
  1319. conn);
  1320.  
  1321. cmd.Parameters.AddWithValue("l", login);
  1322. cmd.Parameters.AddWithValue("p", password);
  1323.  
  1324. using var reader = cmd.ExecuteReader();
  1325.  
  1326. if (reader.Read())
  1327. {
  1328. return new User
  1329. {
  1330. Id = reader.GetInt32(0),
  1331. Login = reader.GetString(1),
  1332. Password = reader.GetString(2),
  1333. Role = NormalizeRole(reader.GetString(3)),
  1334. FullName = reader.GetString(4)
  1335. };
  1336. }
  1337.  
  1338. return null;
  1339. }
  1340.  
  1341. public List<Product> GetProducts()
  1342. {
  1343. var list = new List<Product>();
  1344.  
  1345. using var conn = OpenConnection();
  1346.  
  1347. using var cmd = new NpgsqlCommand(
  1348. """
  1349. SELECT product_id, article, category_id, supplier_id, type_products_id, price, discount, description, photo
  1350. FROM products
  1351. ORDER BY product_id
  1352. """,
  1353. conn);
  1354.  
  1355. using var reader = cmd.ExecuteReader();
  1356.  
  1357. while (reader.Read())
  1358. {
  1359. list.Add(new Product
  1360. {
  1361. ProductId = reader.GetInt32(0),
  1362. Article = reader.GetString(1),
  1363. CategoryId = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
  1364. SupplierId = reader.IsDBNull(3) ? 0 : reader.GetInt32(3),
  1365. TypeProductId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
  1366. Price = reader.GetDecimal(5),
  1367. Discount = reader.IsDBNull(6) ? "0" : reader.GetString(6),
  1368. Description = reader.IsDBNull(7) ? "" : reader.GetString(7),
  1369. Photo = reader.IsDBNull(8) ? "" : reader.GetString(8)
  1370. });
  1371. }
  1372.  
  1373. return list;
  1374. }
  1375.  
  1376. public void AddProduct(Product product)
  1377. {
  1378. using var conn = OpenConnection();
  1379. using var cmd = new NpgsqlCommand(
  1380. """
  1381. INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo)
  1382. VALUES (@article, @categoryId, @supplierId, @typeId, @price, @discount, @description, @photo)
  1383. """,
  1384. conn);
  1385. FillProductParameters(cmd, product);
  1386. cmd.ExecuteNonQuery();
  1387. }
  1388.  
  1389. public void UpdateProduct(Product product)
  1390. {
  1391. using var conn = OpenConnection();
  1392. using var cmd = new NpgsqlCommand(
  1393. """
  1394. UPDATE products
  1395. SET article = @article,
  1396. category_id = @categoryId,
  1397. supplier_id = @supplierId,
  1398. type_products_id = @typeId,
  1399. price = @price,
  1400. discount = @discount,
  1401. description = @description,
  1402. photo = @photo
  1403. WHERE product_id = @id
  1404. """,
  1405. conn);
  1406. FillProductParameters(cmd, product);
  1407. cmd.Parameters.AddWithValue("id", product.ProductId);
  1408. cmd.ExecuteNonQuery();
  1409. }
  1410.  
  1411. public void DeleteProduct(int productId)
  1412. {
  1413. using var conn = OpenConnection();
  1414. using var cmd = new NpgsqlCommand("DELETE FROM products WHERE product_id = @id", conn);
  1415. cmd.Parameters.AddWithValue("id", productId);
  1416. cmd.ExecuteNonQuery();
  1417. }
  1418.  
  1419. public List<Order> GetOrders()
  1420. {
  1421. var list = new List<Order>();
  1422. using var conn = OpenConnection();
  1423. using var cmd = new NpgsqlCommand(
  1424. """
  1425. SELECT order_id, order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum
  1426. FROM orders
  1427. ORDER BY order_id
  1428. """,
  1429. conn);
  1430. using var reader = cmd.ExecuteReader();
  1431. while (reader.Read())
  1432. {
  1433. list.Add(new Order
  1434. {
  1435. OrderId = reader.GetInt32(0),
  1436. OrderArticle = reader.IsDBNull(1) ? "" : reader.GetString(1),
  1437. DateOrder = reader.IsDBNull(2) ? "" : reader.GetDateTime(2).ToString("yyyy-MM-dd"),
  1438. DateDelivery = reader.IsDBNull(3) ? "" : reader.GetDateTime(3).ToString("yyyy-MM-dd"),
  1439. PickUpPointId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
  1440. Code = reader.IsDBNull(5) ? "" : reader.GetString(5),
  1441. StatusOrder = reader.IsDBNull(6) ? "Новый" : reader.GetString(6),
  1442. UserId = reader.IsDBNull(7) ? 0 : reader.GetInt32(7),
  1443. TotalSum = reader.IsDBNull(8) ? 0m : reader.GetDecimal(8)
  1444. });
  1445. }
  1446.  
  1447. return list;
  1448. }
  1449.  
  1450. public void AddOrder(Order order)
  1451. {
  1452. using var conn = OpenConnection();
  1453. using var cmd = new NpgsqlCommand(
  1454. """
  1455. INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum)
  1456. VALUES (@article, @dateOrder, @dateDelivery, @pickUpPointId, @code, @status, @userId, @totalSum)
  1457. """,
  1458. conn);
  1459. FillOrderParameters(cmd, order);
  1460. cmd.ExecuteNonQuery();
  1461. }
  1462.  
  1463. public void UpdateOrder(Order order)
  1464. {
  1465. using var conn = OpenConnection();
  1466. using var cmd = new NpgsqlCommand(
  1467. """
  1468. UPDATE orders
  1469. SET order_article = @article,
  1470. date_order = @dateOrder,
  1471. date_delivery = @dateDelivery,
  1472. pick_up_point_id = @pickUpPointId,
  1473. code = @code,
  1474. status_order = @status,
  1475. user_id = @userId,
  1476. total_sum = @totalSum
  1477. WHERE order_id = @id
  1478. """,
  1479. conn);
  1480. FillOrderParameters(cmd, order);
  1481. cmd.Parameters.AddWithValue("id", order.OrderId);
  1482. cmd.ExecuteNonQuery();
  1483. }
  1484.  
  1485. public void DeleteOrder(int orderId)
  1486. {
  1487. using var conn = OpenConnection();
  1488. using var cmd = new NpgsqlCommand("DELETE FROM orders WHERE order_id = @id", conn);
  1489. cmd.Parameters.AddWithValue("id", orderId);
  1490. cmd.ExecuteNonQuery();
  1491. }
  1492.  
  1493. private static void FillProductParameters(NpgsqlCommand cmd, Product product)
  1494. {
  1495. cmd.Parameters.AddWithValue("article", product.Article);
  1496. cmd.Parameters.AddWithValue("categoryId", product.CategoryId == 0 ? DBNull.Value : product.CategoryId);
  1497. cmd.Parameters.AddWithValue("supplierId", product.SupplierId == 0 ? DBNull.Value : product.SupplierId);
  1498. cmd.Parameters.AddWithValue("typeId", product.TypeProductId == 0 ? DBNull.Value : product.TypeProductId);
  1499. cmd.Parameters.AddWithValue("price", product.Price);
  1500. cmd.Parameters.AddWithValue("discount", product.Discount);
  1501. cmd.Parameters.AddWithValue("description", string.IsNullOrWhiteSpace(product.Description) ? DBNull.Value : product.Description);
  1502. cmd.Parameters.AddWithValue("photo", string.IsNullOrWhiteSpace(product.Photo) ? DBNull.Value : product.Photo);
  1503. }
  1504.  
  1505. private static void FillOrderParameters(NpgsqlCommand cmd, Order order)
  1506. {
  1507. cmd.Parameters.AddWithValue("article", string.IsNullOrWhiteSpace(order.OrderArticle) ? DBNull.Value : order.OrderArticle);
  1508. cmd.Parameters.AddWithValue("dateOrder", ParseDateOrDbNull(order.DateOrder));
  1509. cmd.Parameters.AddWithValue("dateDelivery", ParseDateOrDbNull(order.DateDelivery));
  1510. cmd.Parameters.AddWithValue("pickUpPointId", order.PickUpPointId == 0 ? DBNull.Value : order.PickUpPointId);
  1511. cmd.Parameters.AddWithValue("code", string.IsNullOrWhiteSpace(order.Code) ? DBNull.Value : order.Code);
  1512. cmd.Parameters.AddWithValue("status", string.IsNullOrWhiteSpace(order.StatusOrder) ? "Новый" : order.StatusOrder);
  1513. cmd.Parameters.AddWithValue("userId", order.UserId == 0 ? DBNull.Value : order.UserId);
  1514. cmd.Parameters.AddWithValue("totalSum", order.TotalSum);
  1515. }
  1516.  
  1517. private static object ParseDateOrDbNull(string date)
  1518. {
  1519. return DateTime.TryParse(date, out var value) ? value : DBNull.Value;
  1520. }
  1521.  
  1522. private static string NormalizeRole(string role)
  1523. {
  1524. var value = role.Trim().ToLowerInvariant();
  1525. return value switch
  1526. {
  1527. "админ" => "admin",
  1528. "администратор" => "admin",
  1529. "manager" => "manager",
  1530. "менеджер" => "manager",
  1531. "client" => "client",
  1532. "клиент" => "client",
  1533. _ => value
  1534. };
  1535. }
  1536.  
  1537. private NpgsqlConnection OpenConnection()
  1538. {
  1539. Exception? lastError = null;
  1540.  
  1541. foreach (var connectionString in _connectionCandidates)
  1542. {
  1543. try
  1544. {
  1545. var connection = new NpgsqlConnection(connectionString);
  1546. connection.Open();
  1547. return connection;
  1548. }
  1549. catch (Exception ex)
  1550. {
  1551. lastError = ex;
  1552. }
  1553. }
  1554.  
  1555. throw new InvalidOperationException(
  1556. "Нет подключения к PostgreSQL. Проверьте логин/пароль/название базы в DbService.cs.",
  1557. lastError);
  1558. }
  1559. }
  1560.  
  1561. ================================================================================
  1562. БАЗА ДАННЫХ: СОЗДАНИЕ + ЗАПОЛНЕНИЕ
  1563. ================================================================================
  1564.  
  1565. --------------------------------------------------------------------------------
  1566. FILE: Database/full_schema_and_seed.sql
  1567. --------------------------------------------------------------------------------
  1568. -- (опционально) создать базу:
  1569. -- CREATE DATABASE "321";
  1570.  
  1571. -- 1) Таблицы
  1572. CREATE TABLE roles (
  1573. role_id SERIAL PRIMARY KEY,
  1574. name_role VARCHAR(50) NOT NULL
  1575. );
  1576.  
  1577. CREATE TABLE users (
  1578. user_id SERIAL PRIMARY KEY,
  1579. role_id INTEGER REFERENCES roles(role_id),
  1580. login VARCHAR(100) NOT NULL UNIQUE,
  1581. password VARCHAR(100) NOT NULL,
  1582. name VARCHAR(100),
  1583. second_name VARCHAR(100),
  1584. patronymic VARCHAR(100)
  1585. );
  1586.  
  1587. CREATE TABLE category_products (
  1588. category_id SERIAL PRIMARY KEY,
  1589. name VARCHAR(100) NOT NULL
  1590. );
  1591.  
  1592. CREATE TABLE suppliers (
  1593. supplier_id SERIAL PRIMARY KEY,
  1594. name VARCHAR(200) NOT NULL
  1595. );
  1596.  
  1597. CREATE TABLE type_products (
  1598. type_products_id SERIAL PRIMARY KEY,
  1599. name VARCHAR(100) NOT NULL
  1600. );
  1601.  
  1602. CREATE TABLE products (
  1603. product_id SERIAL PRIMARY KEY,
  1604. article VARCHAR(50) NOT NULL,
  1605. category_id INTEGER REFERENCES category_products(category_id),
  1606. supplier_id INTEGER REFERENCES suppliers(supplier_id),
  1607. type_products_id INTEGER REFERENCES type_products(type_products_id),
  1608. price DECIMAL(10,2) NOT NULL DEFAULT 0,
  1609. discount VARCHAR(10) DEFAULT '0',
  1610. description TEXT,
  1611. photo VARCHAR(255)
  1612. );
  1613.  
  1614. CREATE TABLE storage (
  1615. store_id SERIAL PRIMARY KEY,
  1616. product_id INTEGER REFERENCES products(product_id),
  1617. count INTEGER NOT NULL DEFAULT 0
  1618. );
  1619.  
  1620. CREATE TABLE pick_up_points (
  1621. pick_up_point_id SERIAL PRIMARY KEY,
  1622. postcode VARCHAR(10),
  1623. city VARCHAR(100),
  1624. street VARCHAR(100),
  1625. num_house VARCHAR(20)
  1626. );
  1627.  
  1628. CREATE TABLE orders (
  1629. order_id SERIAL PRIMARY KEY,
  1630. order_article TEXT,
  1631. date_order DATE,
  1632. date_delivery DATE,
  1633. pick_up_point_id INTEGER REFERENCES pick_up_points(pick_up_point_id),
  1634. code VARCHAR(20),
  1635. status_order VARCHAR(50) DEFAULT 'Новый',
  1636. user_id INTEGER REFERENCES users(user_id),
  1637. total_sum DECIMAL(10,2) DEFAULT 0
  1638. );
  1639.  
  1640. CREATE TABLE order_items (
  1641. item_id SERIAL PRIMARY KEY,
  1642. order_id INTEGER REFERENCES orders(order_id) ON DELETE CASCADE,
  1643. product_id INTEGER REFERENCES products(product_id),
  1644. quantity INTEGER NOT NULL DEFAULT 1
  1645. );
  1646.  
  1647. -- 2) Заполнение
  1648. INSERT INTO roles (name_role) VALUES
  1649. ('Администратор'),
  1650. ('Менеджер'),
  1651. ('Клиент');
  1652.  
  1653. INSERT INTO users (role_id, login, password, name, second_name, patronymic) VALUES
  1654. (1, 'admin', 'admin123', 'Иван', 'Админов', 'Админович'),
  1655. (2, 'manager1', 'manager123', 'Петр', 'Менеджеров', 'Петрович'),
  1656. (3, 'client1', 'client123', 'Сидор', 'Сидоров', 'Сидорович'),
  1657. (3, 'client2', 'client456', 'Анна', 'Иванова', 'Алексеевна');
  1658.  
  1659. INSERT INTO category_products (name) VALUES
  1660. ('Электроника'),
  1661. ('Одежда'),
  1662. ('Аксессуары'),
  1663. ('Дом и сад');
  1664.  
  1665. INSERT INTO suppliers (name) VALUES
  1666. ('ООО "ТехноПром"'),
  1667. ('ЗАО "Текстиль-Люкс"'),
  1668. ('ИП Сидоров А.А.'),
  1669. ('ООО "Глобал Импорт"');
  1670.  
  1671. INSERT INTO type_products (name) VALUES
  1672. ('Новинка'),
  1673. ('Популярный'),
  1674. ('Распродажа'),
  1675. ('Стандартный');
  1676.  
  1677. INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo) VALUES
  1678. ('EL-001', 1, 1, 2, 59990.00, '10', 'Флагманский смартфон с отличной камерой', '/images/phone1.jpg'),
  1679. ('CL-002', 2, 2, 1, 3500.00, '0', 'Мужская хлопковая футболка белого цвета', '/images/tshirt_white.jpg'),
  1680. ('AC-003', 3, 3, 4, 1500.00, '5', 'Кожаный ремень классический', '/images/belt_black.jpg'),
  1681. ('EL-004', 1, 1, 3, 4500.00, '20', 'Беспроводные наушники с шумоподавлением', '/images/headphones.jpg'),
  1682. ('HS-005', 4, 4, 4, 2500.00, '0', 'Настольная лампа с регулировкой яркости', '/images/lamp.jpg');
  1683.  
  1684. INSERT INTO storage (product_id, count) VALUES
  1685. (1, 50),
  1686. (2, 200),
  1687. (3, 75),
  1688. (4, 15),
  1689. (5, 120);
  1690.  
  1691. INSERT INTO pick_up_points (postcode, city, street, num_house) VALUES
  1692. ('101000', 'Москва', 'ул. Тверская', '10'),
  1693. ('190000', 'Санкт-Петербург', 'Невский пр.', '25'),
  1694. ('420000', 'Казань', 'ул. Баумана', '15/2');
  1695.  
  1696. INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum) VALUES
  1697. ('ORD-2023-001', '2023-10-01', '2023-10-05', 1, '12345', 'Выдан', 3, 53991.00),
  1698. ('ORD-2023-002', '2023-10-15', '2023-10-20', 2, '54321', 'В пути', 4, 4925.00),
  1699. ('ORD-2023-003', '2023-10-20', '2023-10-25', 1, '11111', 'Новый', 3, 3600.00);
  1700.  
  1701. INSERT INTO order_items (order_id, product_id, quantity) VALUES
  1702. (1, 1, 1),
  1703. (2, 2, 1),
  1704. (2, 3, 1),
  1705. (3, 4, 1);
  1706.  
  1707. ================================================================================
  1708. КОНЕЦ ФАЙЛА
  1709. ================================================================================
RAW Paste Data Copied