================================================================================
PROJECT FULL SOURCE DUMP — AvaloniaApplication1
В этом файле собран весь исходный код проекта на момент создания.
Не включено: папки bin/, obj/ и прочие артефакты сборки.
================================================================================
--------------------------------------------------------------------------------
FILE: Packages.txt
--------------------------------------------------------------------------------
Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent — одна версия.
Npgsql — PostgreSQL.
Строка подключения: services/DbService.cs
SQL-скрипт: Database/schema.sql
--------------------------------------------------------------------------------
FILE: AvaloniaApplication1.sln
--------------------------------------------------------------------------------
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaApplication1", "AvaloniaApplication1.csproj", "{4478C37E-E173-8721-0BB7-2A97BAB6EC35}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4478C37E-E173-8721-0BB7-2A97BAB6EC35}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0BC0D786-F44D-4044-B8DF-051712874244}
EndGlobalSection
EndGlobal
--------------------------------------------------------------------------------
FILE: AvaloniaApplication1.csproj
--------------------------------------------------------------------------------
WinExe
net8.0
enable
app.manifest
AvaloniaApplication1
--------------------------------------------------------------------------------
FILE: app.manifest
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Program.cs
--------------------------------------------------------------------------------
using System;
using Avalonia;
namespace AvaloniaApplication1;
internal static class Program
{
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure()
.UsePlatformDetect()
.LogToTrace();
}
--------------------------------------------------------------------------------
FILE: App.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: App.axaml.cs
--------------------------------------------------------------------------------
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using AvaloniaApplication1.Views;
namespace AvaloniaApplication1;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new LoginWindow();
base.OnFrameworkInitializationCompleted();
}
}
--------------------------------------------------------------------------------
FILE: Models/User.cs
--------------------------------------------------------------------------------
namespace AvaloniaApplication1.Models;
public sealed class User
{
public int Id { get; set; }
public string Login { get; set; } = "";
public string Password { get; set; } = "";
public string Role { get; set; } = "";
public string FullName { get; set; } = "";
}
--------------------------------------------------------------------------------
FILE: Models/Product.cs
--------------------------------------------------------------------------------
namespace AvaloniaApplication1.Models;
public sealed class Product
{
public int ProductId { get; set; }
public string Article { get; set; } = "";
public int CategoryId { get; set; }
public int SupplierId { get; set; }
public int TypeProductId { get; set; }
public decimal Price { get; set; }
public string Discount { get; set; } = "0";
public string Description { get; set; } = "";
public string Photo { get; set; } = "";
public decimal FinalPrice
{
get
{
if (!decimal.TryParse(Discount.Replace("%", ""), out var discountValue))
{
discountValue = 0;
}
return Price * (100m - discountValue) / 100m;
}
}
}
--------------------------------------------------------------------------------
FILE: Models/Order.cs
--------------------------------------------------------------------------------
namespace AvaloniaApplication1.Models;
public sealed class Order
{
public int OrderId { get; set; }
public string OrderArticle { get; set; } = "";
public string DateOrder { get; set; } = "";
public string DateDelivery { get; set; } = "";
public int PickUpPointId { get; set; }
public string Code { get; set; } = "";
public string StatusOrder { get; set; } = "Новый";
public int UserId { get; set; }
public decimal TotalSum { get; set; }
}
--------------------------------------------------------------------------------
FILE: services/DbService.cs
--------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using AvaloniaApplication1.Models;
using Npgsql;
namespace AvaloniaApplication1.Services;
public sealed class DbService
{
private readonly string _connectionString =
"Host=localhost;Port=5432;Username=postgres;Password=postgres;Database=exam_db";
public User? Login(string login, string password)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
SELECT u.user_id,
u.login,
u.password,
COALESCE(r.name_role, 'client') AS role_name,
TRIM(COALESCE(u.name, '') || ' ' || COALESCE(u.second_name, '') || ' ' || COALESCE(u.patronymic, '')) AS full_name
FROM users u
LEFT JOIN roles r ON r.role_id = u.role_id
WHERE u.login = @l AND u.password = @p
""",
conn);
cmd.Parameters.AddWithValue("l", login);
cmd.Parameters.AddWithValue("p", password);
using var reader = cmd.ExecuteReader();
if (reader.Read())
{
return new User
{
Id = reader.GetInt32(0),
Login = reader.GetString(1),
Password = reader.GetString(2),
Role = NormalizeRole(reader.GetString(3)),
FullName = reader.GetString(4)
};
}
return null;
}
public List GetProducts()
{
var list = new List();
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
SELECT product_id, article, category_id, supplier_id, type_products_id, price, discount, description, photo
FROM products
ORDER BY product_id
""",
conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new Product
{
ProductId = reader.GetInt32(0),
Article = reader.GetString(1),
CategoryId = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
SupplierId = reader.IsDBNull(3) ? 0 : reader.GetInt32(3),
TypeProductId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
Price = reader.GetDecimal(5),
Discount = reader.IsDBNull(6) ? "0" : reader.GetString(6),
Description = reader.IsDBNull(7) ? "" : reader.GetString(7),
Photo = reader.IsDBNull(8) ? "" : reader.GetString(8)
});
}
return list;
}
public void AddProduct(Product product)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo)
VALUES (@article, @categoryId, @supplierId, @typeId, @price, @discount, @description, @photo)
""",
conn);
FillProductParameters(cmd, product);
cmd.ExecuteNonQuery();
}
public void UpdateProduct(Product product)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
UPDATE products
SET article = @article,
category_id = @categoryId,
supplier_id = @supplierId,
type_products_id = @typeId,
price = @price,
discount = @discount,
description = @description,
photo = @photo
WHERE product_id = @id
""",
conn);
FillProductParameters(cmd, product);
cmd.Parameters.AddWithValue("id", product.ProductId);
cmd.ExecuteNonQuery();
}
public void DeleteProduct(int productId)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand("DELETE FROM products WHERE product_id = @id", conn);
cmd.Parameters.AddWithValue("id", productId);
cmd.ExecuteNonQuery();
}
public List GetOrders()
{
var list = new List();
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
SELECT order_id, order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum
FROM orders
ORDER BY order_id
""",
conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new Order
{
OrderId = reader.GetInt32(0),
OrderArticle = reader.IsDBNull(1) ? "" : reader.GetString(1),
DateOrder = reader.IsDBNull(2) ? "" : reader.GetDateTime(2).ToString("yyyy-MM-dd"),
DateDelivery = reader.IsDBNull(3) ? "" : reader.GetDateTime(3).ToString("yyyy-MM-dd"),
PickUpPointId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
Code = reader.IsDBNull(5) ? "" : reader.GetString(5),
StatusOrder = reader.IsDBNull(6) ? "Новый" : reader.GetString(6),
UserId = reader.IsDBNull(7) ? 0 : reader.GetInt32(7),
TotalSum = reader.IsDBNull(8) ? 0m : reader.GetDecimal(8)
});
}
return list;
}
public void AddOrder(Order order)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum)
VALUES (@article, @dateOrder, @dateDelivery, @pickUpPointId, @code, @status, @userId, @totalSum)
""",
conn);
FillOrderParameters(cmd, order);
cmd.ExecuteNonQuery();
}
public void UpdateOrder(Order order)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand(
"""
UPDATE orders
SET order_article = @article,
date_order = @dateOrder,
date_delivery = @dateDelivery,
pick_up_point_id = @pickUpPointId,
code = @code,
status_order = @status,
user_id = @userId,
total_sum = @totalSum
WHERE order_id = @id
""",
conn);
FillOrderParameters(cmd, order);
cmd.Parameters.AddWithValue("id", order.OrderId);
cmd.ExecuteNonQuery();
}
public void DeleteOrder(int orderId)
{
using var conn = new NpgsqlConnection(_connectionString);
conn.Open();
using var cmd = new NpgsqlCommand("DELETE FROM orders WHERE order_id = @id", conn);
cmd.Parameters.AddWithValue("id", orderId);
cmd.ExecuteNonQuery();
}
private static void FillProductParameters(NpgsqlCommand cmd, Product product)
{
cmd.Parameters.AddWithValue("article", product.Article);
cmd.Parameters.AddWithValue("categoryId", product.CategoryId == 0 ? DBNull.Value : product.CategoryId);
cmd.Parameters.AddWithValue("supplierId", product.SupplierId == 0 ? DBNull.Value : product.SupplierId);
cmd.Parameters.AddWithValue("typeId", product.TypeProductId == 0 ? DBNull.Value : product.TypeProductId);
cmd.Parameters.AddWithValue("price", product.Price);
cmd.Parameters.AddWithValue("discount", product.Discount);
cmd.Parameters.AddWithValue("description", string.IsNullOrWhiteSpace(product.Description) ? DBNull.Value : product.Description);
cmd.Parameters.AddWithValue("photo", string.IsNullOrWhiteSpace(product.Photo) ? DBNull.Value : product.Photo);
}
private static void FillOrderParameters(NpgsqlCommand cmd, Order order)
{
cmd.Parameters.AddWithValue("article", string.IsNullOrWhiteSpace(order.OrderArticle) ? DBNull.Value : order.OrderArticle);
cmd.Parameters.AddWithValue("dateOrder", ParseDateOrDbNull(order.DateOrder));
cmd.Parameters.AddWithValue("dateDelivery", ParseDateOrDbNull(order.DateDelivery));
cmd.Parameters.AddWithValue("pickUpPointId", order.PickUpPointId == 0 ? DBNull.Value : order.PickUpPointId);
cmd.Parameters.AddWithValue("code", string.IsNullOrWhiteSpace(order.Code) ? DBNull.Value : order.Code);
cmd.Parameters.AddWithValue("status", string.IsNullOrWhiteSpace(order.StatusOrder) ? "Новый" : order.StatusOrder);
cmd.Parameters.AddWithValue("userId", order.UserId == 0 ? DBNull.Value : order.UserId);
cmd.Parameters.AddWithValue("totalSum", order.TotalSum);
}
private static object ParseDateOrDbNull(string date)
{
return DateTime.TryParse(date, out var value) ? value : DBNull.Value;
}
private static string NormalizeRole(string role)
{
var value = role.Trim().ToLowerInvariant();
return value switch
{
"админ" => "admin",
"администратор" => "admin",
"manager" => "manager",
"менеджер" => "manager",
"client" => "client",
"клиент" => "client",
_ => value
};
}
}
--------------------------------------------------------------------------------
FILE: services/bdService.cs
--------------------------------------------------------------------------------
using System.Collections.Generic;
using AvaloniaApplication1.Models;
using Npgsql;
namespace AvaloniaApplication.service
{
}
--------------------------------------------------------------------------------
FILE: Views/LoginWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/LoginWindow.axaml.cs
--------------------------------------------------------------------------------
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
using AvaloniaApplication1.Services;
namespace AvaloniaApplication1.Views;
public partial class LoginWindow : Window
{
private readonly DbService _dbService = new();
public LoginWindow()
{
InitializeComponent();
}
private void Login_Click(object? sender, RoutedEventArgs e)
{
errorText.Text = string.Empty;
var login = loginBox.Text?.Trim() ?? string.Empty;
var password = passwordBox.Text?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
{
errorText.Text = "Введите логин и пароль.";
return;
}
try
{
var user = _dbService.Login(login, password);
if (user is null)
{
errorText.Text = "Неверный логин или пароль.";
return;
}
OpenRoleMenu(user);
}
catch (Exception ex)
{
errorText.Text = $"Ошибка подключения к БД: {ex.Message}";
}
}
private void Guest_Click(object? sender, RoutedEventArgs e)
{
errorText.Text = string.Empty;
OpenRoleMenu(new User
{
Login = "guest",
FullName = "Гость",
Role = "client"
});
}
private void OpenRoleMenu(User user)
{
var roleWindow = new RoleWindow(user, _dbService);
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = roleWindow;
}
roleWindow.Show();
Close();
}
}
--------------------------------------------------------------------------------
FILE: Views/RoleWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/RoleWindow.axaml.cs
--------------------------------------------------------------------------------
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
using AvaloniaApplication1.Services;
namespace AvaloniaApplication1.Views;
public partial class RoleWindow : Window
{
private readonly User _user;
private readonly DbService _dbService;
public RoleWindow(User user, DbService dbService)
{
_user = user;
_dbService = dbService;
InitializeComponent();
helloText.Text = $"Пользователь: {(_user.FullName.Trim() == string.Empty ? _user.Login : _user.FullName)}";
ordersButton.IsVisible = IsManagerOrAdmin(_user.Role);
}
private async void OpenProducts_Click(object? sender, RoutedEventArgs e)
{
await new ProductsWindow(_user, _dbService).ShowDialog(this);
}
private async void OpenOrders_Click(object? sender, RoutedEventArgs e)
{
await new OrdersWindow(_user, _dbService).ShowDialog(this);
}
private void Exit_Click(object? sender, RoutedEventArgs e)
{
var login = new LoginWindow();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = login;
}
login.Show();
Close();
}
private static bool IsManagerOrAdmin(string role)
{
return role is "manager" or "admin";
}
}
--------------------------------------------------------------------------------
FILE: Views/ProductsWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/ProductsWindow.axaml.cs
--------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
using AvaloniaApplication1.Services;
namespace AvaloniaApplication1.Views;
public partial class ProductsWindow : Window
{
private readonly User _user;
private readonly DbService _dbService;
private List _products = [];
public ProductsWindow(User user, DbService dbService)
{
_user = user;
_dbService = dbService;
InitializeComponent();
titleText.Text = $"Товары ({RoleTitle(_user.Role)})";
managePanel.IsVisible = IsManagerOrAdmin(_user.Role);
LoadProducts();
}
private void LoadProducts()
{
try
{
_products = _dbService.GetProducts();
productsItemsControl.ItemsSource = _products;
productsComboBox.ItemsSource = _products;
if (_products.Count > 0)
{
productsComboBox.SelectedIndex = 0;
}
}
catch (Exception ex)
{
_ = ShowError(ex.Message);
}
}
private async void AddProduct_Click(object? sender, RoutedEventArgs e)
{
var editWindow = new ProductEditWindow(new Product());
var result = await editWindow.ShowDialog(this);
if (result is null)
{
return;
}
try
{
_dbService.AddProduct(result);
LoadProducts();
}
catch (Exception ex)
{
_ = ShowError(ex.Message);
}
}
private async void EditProduct_Click(object? sender, RoutedEventArgs e)
{
if (productsComboBox.SelectedItem is not Product selected)
{
return;
}
var clone = new Product
{
ProductId = selected.ProductId,
Article = selected.Article,
CategoryId = selected.CategoryId,
SupplierId = selected.SupplierId,
TypeProductId = selected.TypeProductId,
Price = selected.Price,
Discount = selected.Discount,
Description = selected.Description,
Photo = selected.Photo
};
var editWindow = new ProductEditWindow(clone);
var result = await editWindow.ShowDialog(this);
if (result is null)
{
return;
}
try
{
_dbService.UpdateProduct(result);
LoadProducts();
}
catch (Exception ex)
{
await ShowError(ex.Message);
}
}
private async void DeleteProduct_Click(object? sender, RoutedEventArgs e)
{
if (productsComboBox.SelectedItem is not Product selected)
{
return;
}
try
{
_dbService.DeleteProduct(selected.ProductId);
LoadProducts();
}
catch (Exception ex)
{
await ShowError(ex.Message);
}
}
private async System.Threading.Tasks.Task ShowError(string message)
{
var dialog = new Window
{
Width = 420,
Height = 180,
Title = "Ошибка"
};
dialog.Content = new StackPanel
{
Margin = new Avalonia.Thickness(16),
Spacing = 12,
Children =
{
new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
new Button
{
Content = "OK",
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
}
}
};
((Button)((StackPanel)dialog.Content).Children[1]).Click += (_, _) => dialog.Close();
await dialog.ShowDialog(this);
}
private static bool IsManagerOrAdmin(string role) => role is "manager" or "admin";
private static string RoleTitle(string role) => role switch
{
"manager" => "менеджер",
"admin" => "админ",
_ => "клиент/гость"
};
}
--------------------------------------------------------------------------------
FILE: Views/ProductEditWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/ProductEditWindow.axaml.cs
--------------------------------------------------------------------------------
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
namespace AvaloniaApplication1.Views;
public partial class ProductEditWindow : Window
{
private readonly Product _source;
public ProductEditWindow(Product product)
{
_source = product;
InitializeComponent();
Fill();
}
private void Fill()
{
articleBox.Text = _source.Article;
categoryBox.Text = _source.CategoryId.ToString();
supplierBox.Text = _source.SupplierId.ToString();
typeBox.Text = _source.TypeProductId.ToString();
priceBox.Text = _source.Price.ToString(CultureInfo.InvariantCulture);
discountBox.Text = _source.Discount;
descriptionBox.Text = _source.Description;
photoBox.Text = _source.Photo;
}
private void Save_Click(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(articleBox.Text))
{
return;
}
if (!decimal.TryParse(priceBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var price))
{
return;
}
var result = new Product
{
ProductId = _source.ProductId,
Article = articleBox.Text.Trim(),
CategoryId = ParseInt(categoryBox.Text),
SupplierId = ParseInt(supplierBox.Text),
TypeProductId = ParseInt(typeBox.Text),
Price = price,
Discount = string.IsNullOrWhiteSpace(discountBox.Text) ? "0" : discountBox.Text.Trim(),
Description = descriptionBox.Text?.Trim() ?? "",
Photo = photoBox.Text?.Trim() ?? ""
};
Close(result);
}
private void Cancel_Click(object? sender, RoutedEventArgs e)
{
Close(null);
}
private static int ParseInt(string? value)
{
return int.TryParse(value, out var parsed) ? parsed : 0;
}
}
--------------------------------------------------------------------------------
FILE: Views/OrdersWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/OrdersWindow.axaml.cs
--------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
using AvaloniaApplication1.Services;
namespace AvaloniaApplication1.Views;
public partial class OrdersWindow : Window
{
private readonly User _user;
private readonly DbService _dbService;
private List _orders = [];
public OrdersWindow(User user, DbService dbService)
{
_user = user;
_dbService = dbService;
InitializeComponent();
titleText.Text = "Панель заказов";
adminActions.IsVisible = _user.Role == "admin";
LoadOrders();
}
private void LoadOrders()
{
try
{
_orders = _dbService.GetOrders();
ordersListBox.ItemsSource = _orders;
}
catch (Exception ex)
{
_ = ShowError(ex.Message);
}
}
private async void AddOrder_Click(object? sender, RoutedEventArgs e)
{
var editor = new OrderEditWindow(new Order());
var result = await editor.ShowDialog(this);
if (result is null)
{
return;
}
try
{
_dbService.AddOrder(result);
LoadOrders();
}
catch (Exception ex)
{
await ShowError(ex.Message);
}
}
private async void EditOrder_Click(object? sender, RoutedEventArgs e)
{
if (ordersListBox.SelectedItem is not Order selected)
{
return;
}
var editor = new OrderEditWindow(new Order
{
OrderId = selected.OrderId,
OrderArticle = selected.OrderArticle,
DateOrder = selected.DateOrder,
DateDelivery = selected.DateDelivery,
PickUpPointId = selected.PickUpPointId,
Code = selected.Code,
StatusOrder = selected.StatusOrder,
UserId = selected.UserId,
TotalSum = selected.TotalSum
});
var result = await editor.ShowDialog(this);
if (result is null)
{
return;
}
try
{
_dbService.UpdateOrder(result);
LoadOrders();
}
catch (Exception ex)
{
await ShowError(ex.Message);
}
}
private async void DeleteOrder_Click(object? sender, RoutedEventArgs e)
{
if (ordersListBox.SelectedItem is not Order selected)
{
return;
}
try
{
_dbService.DeleteOrder(selected.OrderId);
LoadOrders();
}
catch (Exception ex)
{
await ShowError(ex.Message);
}
}
private async System.Threading.Tasks.Task ShowError(string message)
{
var dialog = new Window
{
Width = 420,
Height = 180,
Title = "Ошибка"
};
dialog.Content = new StackPanel
{
Margin = new Avalonia.Thickness(16),
Spacing = 12,
Children =
{
new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
new Button
{
Content = "OK",
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
}
}
};
((Button)((StackPanel)dialog.Content).Children[1]).Click += (_, _) => dialog.Close();
await dialog.ShowDialog(this);
}
}
--------------------------------------------------------------------------------
FILE: Views/OrderEditWindow.axaml
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
FILE: Views/OrderEditWindow.axaml.cs
--------------------------------------------------------------------------------
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Interactivity;
using AvaloniaApplication1.Models;
namespace AvaloniaApplication1.Views;
public partial class OrderEditWindow : Window
{
private readonly Order _source;
public OrderEditWindow(Order order)
{
_source = order;
InitializeComponent();
Fill();
}
private void Fill()
{
articleBox.Text = _source.OrderArticle;
dateOrderBox.Text = _source.DateOrder;
dateDeliveryBox.Text = _source.DateDelivery;
pickUpBox.Text = _source.PickUpPointId.ToString();
codeBox.Text = _source.Code;
statusBox.Text = _source.StatusOrder;
userBox.Text = _source.UserId.ToString();
sumBox.Text = _source.TotalSum.ToString(CultureInfo.InvariantCulture);
}
private void Save_Click(object? sender, RoutedEventArgs e)
{
if (!decimal.TryParse(sumBox.Text, NumberStyles.Any, CultureInfo.InvariantCulture, out var sum))
{
return;
}
var result = new Order
{
OrderId = _source.OrderId,
OrderArticle = articleBox.Text?.Trim() ?? "",
DateOrder = dateOrderBox.Text?.Trim() ?? "",
DateDelivery = dateDeliveryBox.Text?.Trim() ?? "",
PickUpPointId = ParseInt(pickUpBox.Text),
Code = codeBox.Text?.Trim() ?? "",
StatusOrder = string.IsNullOrWhiteSpace(statusBox.Text) ? "Новый" : statusBox.Text.Trim(),
UserId = ParseInt(userBox.Text),
TotalSum = sum
};
Close(result);
}
private void Cancel_Click(object? sender, RoutedEventArgs e)
{
Close(null);
}
private static int ParseInt(string? value)
{
return int.TryParse(value, out var parsed) ? parsed : 0;
}
}
================================================================================
ОБНОВЛЕНИЕ: АКТУАЛЬНЫЙ DbService.cs
================================================================================
--------------------------------------------------------------------------------
FILE: services/DbService.cs (UPDATED)
--------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using AvaloniaApplication1.Models;
using Npgsql;
namespace AvaloniaApplication1.Services;
public sealed class DbService
{
private readonly string[] _connectionCandidates =
[
"Host=localhost;Port=5432;Username=postgres;Password=123;Database=321"
];
public User? Login(string login, string password)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
SELECT u.user_id,
u.login,
u.password,
COALESCE(r.name_role, 'client') AS role_name,
TRIM(COALESCE(u.name, '') || ' ' || COALESCE(u.second_name, '') || ' ' || COALESCE(u.patronymic, '')) AS full_name
FROM users u
LEFT JOIN roles r ON r.role_id = u.role_id
WHERE u.login = @l AND u.password = @p
""",
conn);
cmd.Parameters.AddWithValue("l", login);
cmd.Parameters.AddWithValue("p", password);
using var reader = cmd.ExecuteReader();
if (reader.Read())
{
return new User
{
Id = reader.GetInt32(0),
Login = reader.GetString(1),
Password = reader.GetString(2),
Role = NormalizeRole(reader.GetString(3)),
FullName = reader.GetString(4)
};
}
return null;
}
public List GetProducts()
{
var list = new List();
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
SELECT product_id, article, category_id, supplier_id, type_products_id, price, discount, description, photo
FROM products
ORDER BY product_id
""",
conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new Product
{
ProductId = reader.GetInt32(0),
Article = reader.GetString(1),
CategoryId = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
SupplierId = reader.IsDBNull(3) ? 0 : reader.GetInt32(3),
TypeProductId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
Price = reader.GetDecimal(5),
Discount = reader.IsDBNull(6) ? "0" : reader.GetString(6),
Description = reader.IsDBNull(7) ? "" : reader.GetString(7),
Photo = reader.IsDBNull(8) ? "" : reader.GetString(8)
});
}
return list;
}
public void AddProduct(Product product)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo)
VALUES (@article, @categoryId, @supplierId, @typeId, @price, @discount, @description, @photo)
""",
conn);
FillProductParameters(cmd, product);
cmd.ExecuteNonQuery();
}
public void UpdateProduct(Product product)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
UPDATE products
SET article = @article,
category_id = @categoryId,
supplier_id = @supplierId,
type_products_id = @typeId,
price = @price,
discount = @discount,
description = @description,
photo = @photo
WHERE product_id = @id
""",
conn);
FillProductParameters(cmd, product);
cmd.Parameters.AddWithValue("id", product.ProductId);
cmd.ExecuteNonQuery();
}
public void DeleteProduct(int productId)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand("DELETE FROM products WHERE product_id = @id", conn);
cmd.Parameters.AddWithValue("id", productId);
cmd.ExecuteNonQuery();
}
public List GetOrders()
{
var list = new List();
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
SELECT order_id, order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum
FROM orders
ORDER BY order_id
""",
conn);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
list.Add(new Order
{
OrderId = reader.GetInt32(0),
OrderArticle = reader.IsDBNull(1) ? "" : reader.GetString(1),
DateOrder = reader.IsDBNull(2) ? "" : reader.GetDateTime(2).ToString("yyyy-MM-dd"),
DateDelivery = reader.IsDBNull(3) ? "" : reader.GetDateTime(3).ToString("yyyy-MM-dd"),
PickUpPointId = reader.IsDBNull(4) ? 0 : reader.GetInt32(4),
Code = reader.IsDBNull(5) ? "" : reader.GetString(5),
StatusOrder = reader.IsDBNull(6) ? "Новый" : reader.GetString(6),
UserId = reader.IsDBNull(7) ? 0 : reader.GetInt32(7),
TotalSum = reader.IsDBNull(8) ? 0m : reader.GetDecimal(8)
});
}
return list;
}
public void AddOrder(Order order)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum)
VALUES (@article, @dateOrder, @dateDelivery, @pickUpPointId, @code, @status, @userId, @totalSum)
""",
conn);
FillOrderParameters(cmd, order);
cmd.ExecuteNonQuery();
}
public void UpdateOrder(Order order)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand(
"""
UPDATE orders
SET order_article = @article,
date_order = @dateOrder,
date_delivery = @dateDelivery,
pick_up_point_id = @pickUpPointId,
code = @code,
status_order = @status,
user_id = @userId,
total_sum = @totalSum
WHERE order_id = @id
""",
conn);
FillOrderParameters(cmd, order);
cmd.Parameters.AddWithValue("id", order.OrderId);
cmd.ExecuteNonQuery();
}
public void DeleteOrder(int orderId)
{
using var conn = OpenConnection();
using var cmd = new NpgsqlCommand("DELETE FROM orders WHERE order_id = @id", conn);
cmd.Parameters.AddWithValue("id", orderId);
cmd.ExecuteNonQuery();
}
private static void FillProductParameters(NpgsqlCommand cmd, Product product)
{
cmd.Parameters.AddWithValue("article", product.Article);
cmd.Parameters.AddWithValue("categoryId", product.CategoryId == 0 ? DBNull.Value : product.CategoryId);
cmd.Parameters.AddWithValue("supplierId", product.SupplierId == 0 ? DBNull.Value : product.SupplierId);
cmd.Parameters.AddWithValue("typeId", product.TypeProductId == 0 ? DBNull.Value : product.TypeProductId);
cmd.Parameters.AddWithValue("price", product.Price);
cmd.Parameters.AddWithValue("discount", product.Discount);
cmd.Parameters.AddWithValue("description", string.IsNullOrWhiteSpace(product.Description) ? DBNull.Value : product.Description);
cmd.Parameters.AddWithValue("photo", string.IsNullOrWhiteSpace(product.Photo) ? DBNull.Value : product.Photo);
}
private static void FillOrderParameters(NpgsqlCommand cmd, Order order)
{
cmd.Parameters.AddWithValue("article", string.IsNullOrWhiteSpace(order.OrderArticle) ? DBNull.Value : order.OrderArticle);
cmd.Parameters.AddWithValue("dateOrder", ParseDateOrDbNull(order.DateOrder));
cmd.Parameters.AddWithValue("dateDelivery", ParseDateOrDbNull(order.DateDelivery));
cmd.Parameters.AddWithValue("pickUpPointId", order.PickUpPointId == 0 ? DBNull.Value : order.PickUpPointId);
cmd.Parameters.AddWithValue("code", string.IsNullOrWhiteSpace(order.Code) ? DBNull.Value : order.Code);
cmd.Parameters.AddWithValue("status", string.IsNullOrWhiteSpace(order.StatusOrder) ? "Новый" : order.StatusOrder);
cmd.Parameters.AddWithValue("userId", order.UserId == 0 ? DBNull.Value : order.UserId);
cmd.Parameters.AddWithValue("totalSum", order.TotalSum);
}
private static object ParseDateOrDbNull(string date)
{
return DateTime.TryParse(date, out var value) ? value : DBNull.Value;
}
private static string NormalizeRole(string role)
{
var value = role.Trim().ToLowerInvariant();
return value switch
{
"админ" => "admin",
"администратор" => "admin",
"manager" => "manager",
"менеджер" => "manager",
"client" => "client",
"клиент" => "client",
_ => value
};
}
private NpgsqlConnection OpenConnection()
{
Exception? lastError = null;
foreach (var connectionString in _connectionCandidates)
{
try
{
var connection = new NpgsqlConnection(connectionString);
connection.Open();
return connection;
}
catch (Exception ex)
{
lastError = ex;
}
}
throw new InvalidOperationException(
"Нет подключения к PostgreSQL. Проверьте логин/пароль/название базы в DbService.cs.",
lastError);
}
}
================================================================================
БАЗА ДАННЫХ: СОЗДАНИЕ + ЗАПОЛНЕНИЕ
================================================================================
--------------------------------------------------------------------------------
FILE: Database/full_schema_and_seed.sql
--------------------------------------------------------------------------------
-- (опционально) создать базу:
-- CREATE DATABASE "321";
-- 1) Таблицы
CREATE TABLE roles (
role_id SERIAL PRIMARY KEY,
name_role VARCHAR(50) NOT NULL
);
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
role_id INTEGER REFERENCES roles(role_id),
login VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
name VARCHAR(100),
second_name VARCHAR(100),
patronymic VARCHAR(100)
);
CREATE TABLE category_products (
category_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE suppliers (
supplier_id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL
);
CREATE TABLE type_products (
type_products_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
article VARCHAR(50) NOT NULL,
category_id INTEGER REFERENCES category_products(category_id),
supplier_id INTEGER REFERENCES suppliers(supplier_id),
type_products_id INTEGER REFERENCES type_products(type_products_id),
price DECIMAL(10,2) NOT NULL DEFAULT 0,
discount VARCHAR(10) DEFAULT '0',
description TEXT,
photo VARCHAR(255)
);
CREATE TABLE storage (
store_id SERIAL PRIMARY KEY,
product_id INTEGER REFERENCES products(product_id),
count INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE pick_up_points (
pick_up_point_id SERIAL PRIMARY KEY,
postcode VARCHAR(10),
city VARCHAR(100),
street VARCHAR(100),
num_house VARCHAR(20)
);
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_article TEXT,
date_order DATE,
date_delivery DATE,
pick_up_point_id INTEGER REFERENCES pick_up_points(pick_up_point_id),
code VARCHAR(20),
status_order VARCHAR(50) DEFAULT 'Новый',
user_id INTEGER REFERENCES users(user_id),
total_sum DECIMAL(10,2) DEFAULT 0
);
CREATE TABLE order_items (
item_id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(order_id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(product_id),
quantity INTEGER NOT NULL DEFAULT 1
);
-- 2) Заполнение
INSERT INTO roles (name_role) VALUES
('Администратор'),
('Менеджер'),
('Клиент');
INSERT INTO users (role_id, login, password, name, second_name, patronymic) VALUES
(1, 'admin', 'admin123', 'Иван', 'Админов', 'Админович'),
(2, 'manager1', 'manager123', 'Петр', 'Менеджеров', 'Петрович'),
(3, 'client1', 'client123', 'Сидор', 'Сидоров', 'Сидорович'),
(3, 'client2', 'client456', 'Анна', 'Иванова', 'Алексеевна');
INSERT INTO category_products (name) VALUES
('Электроника'),
('Одежда'),
('Аксессуары'),
('Дом и сад');
INSERT INTO suppliers (name) VALUES
('ООО "ТехноПром"'),
('ЗАО "Текстиль-Люкс"'),
('ИП Сидоров А.А.'),
('ООО "Глобал Импорт"');
INSERT INTO type_products (name) VALUES
('Новинка'),
('Популярный'),
('Распродажа'),
('Стандартный');
INSERT INTO products (article, category_id, supplier_id, type_products_id, price, discount, description, photo) VALUES
('EL-001', 1, 1, 2, 59990.00, '10', 'Флагманский смартфон с отличной камерой', '/images/phone1.jpg'),
('CL-002', 2, 2, 1, 3500.00, '0', 'Мужская хлопковая футболка белого цвета', '/images/tshirt_white.jpg'),
('AC-003', 3, 3, 4, 1500.00, '5', 'Кожаный ремень классический', '/images/belt_black.jpg'),
('EL-004', 1, 1, 3, 4500.00, '20', 'Беспроводные наушники с шумоподавлением', '/images/headphones.jpg'),
('HS-005', 4, 4, 4, 2500.00, '0', 'Настольная лампа с регулировкой яркости', '/images/lamp.jpg');
INSERT INTO storage (product_id, count) VALUES
(1, 50),
(2, 200),
(3, 75),
(4, 15),
(5, 120);
INSERT INTO pick_up_points (postcode, city, street, num_house) VALUES
('101000', 'Москва', 'ул. Тверская', '10'),
('190000', 'Санкт-Петербург', 'Невский пр.', '25'),
('420000', 'Казань', 'ул. Баумана', '15/2');
INSERT INTO orders (order_article, date_order, date_delivery, pick_up_point_id, code, status_order, user_id, total_sum) VALUES
('ORD-2023-001', '2023-10-01', '2023-10-05', 1, '12345', 'Выдан', 3, 53991.00),
('ORD-2023-002', '2023-10-15', '2023-10-20', 2, '54321', 'В пути', 4, 4925.00),
('ORD-2023-003', '2023-10-20', '2023-10-25', 1, '11111', 'Новый', 3, 3600.00);
INSERT INTO order_items (order_id, product_id, quantity) VALUES
(1, 1, 1),
(2, 2, 1),
(2, 3, 1),
(3, 4, 1);
================================================================================
КОНЕЦ ФАЙЛА
================================================================================