/
DataTimeModule
/
DateTime
Обзор
Документация
Войти
/
DataTimeModule
/
DateTime
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
GUIDE.txt
948 строк
31 KB
Reeltell
upload
27 май 2026, 01:50
27 май 2026, 01:50
f282598
Код
Авторство
О чём код?
Модуль 2. Авторизация и список товаров 1. Подготовка проекта Создать WinForms проект. NuGet: Microsoft.Data.SqlClient Добавить папку: Images Положить: Icon.png picture.png 1.jpg ... 10.jpg В .csproj можно добавить: <ItemGroup> <Content Include="Images\*.*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> </ItemGroup> 2. Db.cs Создать файл Db.cs. using Microsoft.Data.SqlClient; using System.Data; namespace demo1; internal static class Db { private const string ConnectionString = "Data Source=localhost;Initial Catalog=demo1;Integrated Security=True;TrustServerCertificate=True"; public static SqlConnection OpenConnection() { var connection = new SqlConnection(ConnectionString); connection.Open(); return connection; } public static void LoadComboBox(ComboBox comboBox, string sql, string display, string value) { using var connection = OpenConnection(); var table = new DataTable(); new SqlDataAdapter(sql, connection).Fill(table); comboBox.DataSource = table; comboBox.DisplayMember = display; comboBox.ValueMember = value; } public static Image GetImage(string? fileName) { string path = Path.Combine(AppContext.BaseDirectory, "Images", fileName?.Trim() ?? ""); if (!File.Exists(path)) { path = Path.Combine(AppContext.BaseDirectory, "Images", "picture.png"); } using var image = Image.FromFile(path); return new Bitmap(image); } } 3. Form1 Компоненты: PictureBox logoPictureBox TextBox loginTextBox TextBox passwordTextBox Button enterButton Button guestButton Свойства: passwordTextBox.PasswordChar = * logoPictureBox.SizeMode = Zoom 4. Form1.cs namespace demo1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); LoadLogo(); } private void LoadLogo() { string path = Path.Combine(AppContext.BaseDirectory, "Images", "Icon.png"); if (File.Exists(path)) { logoPictureBox.Image = Image.FromFile(path); } } private void enterButton_Click(object sender, EventArgs e) { string login = loginTextBox.Text.Trim(); string password = passwordTextBox.Text.Trim(); if (login == "" || password == "") { MessageBox.Show("Введите логин и пароль.", "Ошибка"); return; } using var connection = Db.OpenConnection(); string sql = """ SELECT TOP 1 u.Surname + N' ' + u.Name + N' ' + ISNULL(u.Patronymic, N'') AS FullName, r.Title AS RoleTitle FROM Users u JOIN Roles r ON u.RoleId = r.RoleId WHERE u.Login = @login AND u.Password = @password """; using var command = new Microsoft.Data.SqlClient.SqlCommand(sql, connection); command.Parameters.AddWithValue("@login", login); command.Parameters.AddWithValue("@password", password); using var reader = command.ExecuteReader(); if (!reader.Read()) { MessageBox.Show("Неверный логин или пароль.", "Ошибка"); return; } OpenProducts(reader["FullName"].ToString()!, reader["RoleTitle"].ToString()!); } private void guestButton_Click(object sender, EventArgs e) { OpenProducts("Гость", "Гость"); } private void OpenProducts(string fullName, string role) { Hide(); var form = new ProductForm(fullName, role); form.FormClosed += (_, _) => Show(); form.Show(); } } } Модуль 2-3. Карточка товара 1. ProductCard Создать: UserControl ProductCard Компоненты: PictureBox photoBox Label infoLabel Label priceLabel Label discountPriceLabel Label discountLabel Button editButton Button deleteButton Свойства: photoBox.SizeMode = Zoom 2. ProductCard.cs namespace demo1 { public partial class ProductCard : UserControl { public event Action<string>? EditProduct; public event Action<string>? DeleteProduct; private string article = ""; public ProductCard() { InitializeComponent(); } public void SetProduct(string article, Image photo, string title, string category, string description, string manufacturer, string provider, decimal price, int discount, int stock, string unit, bool isAdmin) { this.article = article; photoBox.Image = photo; editButton.Visible = isAdmin; deleteButton.Visible = isAdmin; infoLabel.Text = $"{category} | {title}\n" + $"Описание товара: {description}\n" + $"Производитель: {manufacturer}\n" + $"Поставщик: {provider}\n" + $"Единица измерения: {unit}\n" + $"Количество на складе: {stock}"; priceLabel.Text = $"Цена: {price:0.00}"; discountLabel.Text = $"Скидка\n{discount}%"; if (discount > 0) { decimal newPrice = price * (100 - discount) / 100; priceLabel.ForeColor = Color.Red; priceLabel.Font = new Font(Font, FontStyle.Strikeout); discountPriceLabel.Text = $"{newPrice:0.00}"; } if (stock == 0) { BackColor = Color.LightBlue; } else if (discount > 15) { BackColor = Color.SeaGreen; } } private void editButton_Click(object sender, EventArgs e) { EditProduct?.Invoke(article); } private void deleteButton_Click(object sender, EventArgs e) { DeleteProduct?.Invoke(article); } } } Модуль 2-3. Список товаров 1. ProductForm Компоненты: Label userLabel Button exitButton TextBox searchTextBox ComboBox providerComboBox ComboBox sortComboBox Button addButton Button ordersButton FlowLayoutPanel productsPanel Свойства: productsPanel.AutoScroll = True productsPanel.FlowDirection = TopDown productsPanel.WrapContents = False providerComboBox.DropDownStyle = DropDownList sortComboBox.DropDownStyle = DropDownList 2. ProductForm.cs using Microsoft.Data.SqlClient; namespace demo1 { public partial class ProductForm : Form { private readonly string role; private ProductEditForm? editForm; public ProductForm(string fullName, string role) { InitializeComponent(); this.role = role; userLabel.Text = $"{role}: {fullName}"; } private bool CanManageProducts => role == "Администратор"; private bool CanFilterProducts => role == "Администратор" || role == "Менеджер"; private void ProductForm_Load(object sender, EventArgs e) { searchTextBox.Visible = CanFilterProducts; providerComboBox.Visible = CanFilterProducts; sortComboBox.Visible = CanFilterProducts; addButton.Visible = CanManageProducts; ordersButton.Visible = CanFilterProducts; sortComboBox.Items.AddRange(new[] { "Без сортировки", "Остаток ↑", "Остаток ↓" }); sortComboBox.SelectedIndex = 0; LoadProviders(); LoadProducts(); } private void LoadProviders() { providerComboBox.Items.Clear(); providerComboBox.Items.Add("Все поставщики"); using var connection = Db.OpenConnection(); using var command = new SqlCommand("SELECT Title FROM Providers ORDER BY Title", connection); using var reader = command.ExecuteReader(); while (reader.Read()) { providerComboBox.Items.Add(reader["Title"].ToString()!.Trim()); } providerComboBox.SelectedIndex = 0; } private void LoadProducts() { productsPanel.Controls.Clear(); using var connection = Db.OpenConnection(); string sql = """ SELECT p.Article, p.Title, c.Title AS Category, p.Description, m.Title AS Manufacturer, pr.Title AS Provider, p.Price, p.Discount, p.StockQuantity, u.Title AS Unit, p.PhotoPath FROM Products p JOIN ProductCategories c ON p.ProductCategoryId = c.ProductCategoryId JOIN Manufacturers m ON p.ManufacturerId = m.ManufacturerId JOIN Providers pr ON p.ProviderId = pr.ProviderId JOIN Units u ON p.UnitId = u.UnitId WHERE (@provider = N'' OR pr.Title = @provider) AND (@search = N'' OR p.Title LIKE @like OR p.Description LIKE @like OR c.Title LIKE @like OR m.Title LIKE @like OR pr.Title LIKE @like OR u.Title LIKE @like) ORDER BY CASE WHEN @sort = 1 THEN p.StockQuantity END, CASE WHEN @sort = 2 THEN p.StockQuantity END DESC, p.Title """; using var command = new SqlCommand(sql, connection); string provider = providerComboBox.SelectedIndex > 0 ? providerComboBox.Text : ""; string search = searchTextBox.Text.Trim(); int sort = sortComboBox.SelectedIndex; command.Parameters.AddWithValue("@provider", provider); command.Parameters.AddWithValue("@search", search); command.Parameters.AddWithValue("@like", $"%{search}%"); command.Parameters.AddWithValue("@sort", sort); using var reader = command.ExecuteReader(); while (reader.Read()) { var card = new ProductCard(); card.SetProduct( reader["Article"].ToString()!, Db.GetImage(reader["PhotoPath"].ToString()), reader["Title"].ToString()!, reader["Category"].ToString()!, reader["Description"].ToString()!, reader["Manufacturer"].ToString()!, reader["Provider"].ToString()!, Convert.ToDecimal(reader["Price"]), Convert.ToInt32(reader["Discount"]), Convert.ToInt32(reader["StockQuantity"]), reader["Unit"].ToString()!, CanManageProducts); card.EditProduct += OpenEditForm; card.DeleteProduct += DeleteProduct; productsPanel.Controls.Add(card); } } private void filter_Changed(object sender, EventArgs e) { if (CanFilterProducts && sortComboBox.SelectedIndex >= 0 && providerComboBox.SelectedIndex >= 0) { LoadProducts(); } } private void addButton_Click(object sender, EventArgs e) { OpenEditForm(""); } private void ordersButton_Click(object sender, EventArgs e) { new OrdersForm(CanManageProducts).ShowDialog(); } private void OpenEditForm(string article) { if (editForm != null) { editForm.Activate(); return; } editForm = new ProductEditForm(article); editForm.FormClosed += (_, _) => { editForm = null; LoadProducts(); }; editForm.Show(); } private void DeleteProduct(string article) { if (MessageBox.Show("Удалить товар?", "Подтверждение", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } try { using var connection = Db.OpenConnection(); using var command = new SqlCommand("DELETE FROM Products WHERE Article = @article", connection); command.Parameters.AddWithValue("@article", article); command.ExecuteNonQuery(); LoadProducts(); } catch { MessageBox.Show("Нельзя удалить товар, который есть в заказе.", "Ошибка"); } } private void exitButton_Click(object sender, EventArgs e) { Close(); } } } Модуль 3. Добавление и редактирование товара 1. ProductEditForm Компоненты: TextBox articleTextBox TextBox titleTextBox ComboBox categoryComboBox TextBox descriptionTextBox ComboBox manufacturerComboBox ComboBox providerComboBox TextBox priceTextBox ComboBox unitComboBox TextBox stockTextBox TextBox discountTextBox PictureBox photoBox Button photoButton Button saveButton Button cancelButton Свойства: articleTextBox.ReadOnly = True descriptionTextBox.Multiline = True photoBox.SizeMode = Zoom categoryComboBox.DropDownStyle = DropDownList manufacturerComboBox.DropDownStyle = DropDownList providerComboBox.DropDownStyle = DropDownList unitComboBox.DropDownStyle = DropDownList 2. ProductEditForm.cs using Microsoft.Data.SqlClient; namespace demo1 { public partial class ProductEditForm : Form { private readonly string article; private string photoPath = ""; private string newPhotoPath = ""; public ProductEditForm(string article) { InitializeComponent(); this.article = article; } private bool IsEdit => article != ""; private void ProductEditForm_Load(object sender, EventArgs e) { LoadComboBoxes(); if (IsEdit) { LoadProduct(); } else { articleLabel.Visible = false; articleTextBox.Visible = false; photoPath = "picture.png"; photoBox.Image = Db.GetImage(photoPath); } } private void LoadComboBoxes() { Db.LoadComboBox(categoryComboBox, "SELECT ProductCategoryId, Title FROM ProductCategories ORDER BY Title", "Title", "ProductCategoryId"); Db.LoadComboBox(manufacturerComboBox, "SELECT ManufacturerId, Title FROM Manufacturers ORDER BY Title", "Title", "ManufacturerId"); Db.LoadComboBox(providerComboBox, "SELECT ProviderId, Title FROM Providers ORDER BY Title", "Title", "ProviderId"); Db.LoadComboBox(unitComboBox, "SELECT UnitId, Title FROM Units ORDER BY Title", "Title", "UnitId"); } private void LoadProduct() { using var connection = Db.OpenConnection(); string sql = "SELECT * FROM Products WHERE Article = @article"; using var command = new SqlCommand(sql, connection); command.Parameters.AddWithValue("@article", article); using var reader = command.ExecuteReader(); if (!reader.Read()) { Close(); return; } articleTextBox.Text = article; titleTextBox.Text = reader["Title"].ToString(); descriptionTextBox.Text = reader["Description"].ToString(); priceTextBox.Text = reader["Price"].ToString(); stockTextBox.Text = reader["StockQuantity"].ToString(); discountTextBox.Text = reader["Discount"].ToString(); categoryComboBox.SelectedValue = Convert.ToInt32(reader["ProductCategoryId"]); manufacturerComboBox.SelectedValue = Convert.ToInt32(reader["ManufacturerId"]); providerComboBox.SelectedValue = Convert.ToInt32(reader["ProviderId"]); unitComboBox.SelectedValue = Convert.ToInt32(reader["UnitId"]); photoPath = reader["PhotoPath"].ToString()!.Trim(); photoBox.Image = Db.GetImage(photoPath); } private void saveButton_Click(object sender, EventArgs e) { if (!decimal.TryParse(priceTextBox.Text, out decimal price) || price < 0 || !int.TryParse(stockTextBox.Text, out int stock) || stock < 0 || !int.TryParse(discountTextBox.Text, out int discount) || discount < 0 || discount > 100) { MessageBox.Show("Проверьте цену, количество и скидку.", "Ошибка"); return; } if (titleTextBox.Text.Trim() == "") { MessageBox.Show("Введите наименование товара.", "Ошибка"); return; } string newArticle = IsEdit ? article : GetNewArticle(); string savedPhoto = SavePhoto(newArticle); using var connection = Db.OpenConnection(); string sql = IsEdit ? """ UPDATE Products SET Title=@title, UnitId=@unit, Price=@price, ProviderId=@provider, ManufacturerId=@manufacturer, ProductCategoryId=@category, Discount=@discount, StockQuantity=@stock, Description=@description, PhotoPath=@photo WHERE Article=@article """ : """ INSERT INTO Products (Article, Title, UnitId, Price, ProviderId, ManufacturerId, ProductCategoryId, Discount, StockQuantity, Description, PhotoPath) VALUES (@article, @title, @unit, @price, @provider, @manufacturer, @category, @discount, @stock, @description, @photo) """; using var command = new SqlCommand(sql, connection); command.Parameters.AddWithValue("@article", newArticle); command.Parameters.AddWithValue("@title", titleTextBox.Text.Trim()); command.Parameters.AddWithValue("@unit", unitComboBox.SelectedValue); command.Parameters.AddWithValue("@price", price); command.Parameters.AddWithValue("@provider", providerComboBox.SelectedValue); command.Parameters.AddWithValue("@manufacturer", manufacturerComboBox.SelectedValue); command.Parameters.AddWithValue("@category", categoryComboBox.SelectedValue); command.Parameters.AddWithValue("@discount", discount); command.Parameters.AddWithValue("@stock", stock); command.Parameters.AddWithValue("@description", descriptionTextBox.Text.Trim()); command.Parameters.AddWithValue("@photo", savedPhoto); command.ExecuteNonQuery(); Close(); } private string GetNewArticle() { using var connection = Db.OpenConnection(); using var command = new SqlCommand("SELECT COUNT(*) + 1 FROM Products", connection); return "NEW" + command.ExecuteScalar(); } private void photoButton_Click(object sender, EventArgs e) { using var dialog = new OpenFileDialog(); dialog.Filter = "Изображения|*.jpg;*.jpeg;*.png"; if (dialog.ShowDialog() == DialogResult.OK) { newPhotoPath = dialog.FileName; using var image = Image.FromFile(newPhotoPath); photoBox.Image = new Bitmap(image); } } private string SavePhoto(string currentArticle) { if (newPhotoPath == "") { return photoPath; } string fileName = currentArticle + Path.GetExtension(newPhotoPath); string path = Path.Combine(AppContext.BaseDirectory, "Images", fileName); using var image = Image.FromFile(newPhotoPath); using var small = new Bitmap(image, new Size(300, 200)); small.Save(path); if (IsEdit && photoPath != "" && photoPath != "picture.png" && photoPath != fileName) { string oldPath = Path.Combine(AppContext.BaseDirectory, "Images", photoPath); if (File.Exists(oldPath)) { File.Delete(oldPath); } } return fileName; } private void cancelButton_Click(object sender, EventArgs e) { Close(); } } } Модуль 4. Карточка заказа 1. OrderCard Создать: UserControl OrderCard Компоненты: Label infoLabel Label deliveryLabel Button editButton Button deleteButton 2. OrderCard.cs namespace demo1 { public partial class OrderCard : UserControl { public event Action<int>? EditOrder; public event Action<int>? DeleteOrder; private int orderId; public OrderCard() { InitializeComponent(); } public void SetOrder(int id, string products, string status, string address, DateTime orderDate, DateTime deliveryDate, bool isAdmin) { orderId = id; editButton.Visible = isAdmin; deleteButton.Visible = isAdmin; infoLabel.Text = $"Артикул заказа: {products}\n" + $"Статус заказа: {status}\n" + $"Адрес пункта выдачи: {address}\n" + $"Дата заказа: {orderDate:dd.MM.yyyy}"; deliveryLabel.Text = $"Дата доставки\n{deliveryDate:dd.MM.yyyy}"; } private void editButton_Click(object sender, EventArgs e) { EditOrder?.Invoke(orderId); } private void deleteButton_Click(object sender, EventArgs e) { DeleteOrder?.Invoke(orderId); } } } Модуль 4. Список заказов 1. OrdersForm Компоненты: Button addButton Button closeButton FlowLayoutPanel ordersPanel Свойства: ordersPanel.AutoScroll = True ordersPanel.FlowDirection = TopDown ordersPanel.WrapContents = False 2. OrdersForm.cs using Microsoft.Data.SqlClient; namespace demo1 { public partial class OrdersForm : Form { private readonly bool isAdmin; public OrdersForm(bool isAdmin) { InitializeComponent(); this.isAdmin = isAdmin; } private void OrdersForm_Load(object sender, EventArgs e) { addButton.Visible = isAdmin; LoadOrders(); } private void LoadOrders() { ordersPanel.Controls.Clear(); using var connection = Db.OpenConnection(); string sql = """ SELECT o.OrderId, o.OrderDate, o.DeliveryDate, pp.Address, os.Title AS Status, (SELECT STRING_AGG(Article, ', ') FROM OrderProducts WHERE OrderId = o.OrderId) AS Products FROM Orders o JOIN PickupPoints pp ON o.PickupPointId = pp.PickupPointId JOIN OrderStatuses os ON o.OrderStatusId = os.OrderStatusId ORDER BY o.OrderId """; using var command = new SqlCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { var card = new OrderCard(); card.SetOrder( Convert.ToInt32(reader["OrderId"]), reader["Products"].ToString()!, reader["Status"].ToString()!, reader["Address"].ToString()!, Convert.ToDateTime(reader["OrderDate"]), Convert.ToDateTime(reader["DeliveryDate"]), isAdmin); card.EditOrder += OpenEditForm; card.DeleteOrder += DeleteOrder; ordersPanel.Controls.Add(card); } } private void addButton_Click(object sender, EventArgs e) { OpenEditForm(0); } private void OpenEditForm(int orderId) { using var form = new OrderEditForm(orderId); form.ShowDialog(); LoadOrders(); } private void DeleteOrder(int orderId) { if (MessageBox.Show("Удалить заказ?", "Подтверждение", MessageBoxButtons.YesNo) != DialogResult.Yes) { return; } using var connection = Db.OpenConnection(); using var command = new SqlCommand( "DELETE FROM OrderProducts WHERE OrderId=@id; DELETE FROM Orders WHERE OrderId=@id", connection); command.Parameters.AddWithValue("@id", orderId); command.ExecuteNonQuery(); LoadOrders(); } private void closeButton_Click(object sender, EventArgs e) { Close(); } } } Модуль 4. Добавление и редактирование заказа 1. OrderEditForm Компоненты: ComboBox articleComboBox ComboBox statusComboBox ComboBox pickupComboBox DateTimePicker orderDatePicker DateTimePicker deliveryDatePicker Button saveButton Button cancelButton Свойства: articleComboBox.DropDownStyle = DropDownList statusComboBox.DropDownStyle = DropDownList pickupComboBox.DropDownStyle = DropDownList 2. OrderEditForm.cs using Microsoft.Data.SqlClient; namespace demo1 { public partial class OrderEditForm : Form { private readonly int orderId; public OrderEditForm(int orderId) { InitializeComponent(); this.orderId = orderId; } private bool IsEdit => orderId != 0; private void OrderEditForm_Load(object sender, EventArgs e) { Db.LoadComboBox(articleComboBox, "SELECT Article FROM Products ORDER BY Article", "Article", "Article"); Db.LoadComboBox(statusComboBox, "SELECT OrderStatusId, Title FROM OrderStatuses ORDER BY Title", "Title", "OrderStatusId"); Db.LoadComboBox(pickupComboBox, "SELECT PickupPointId, Address FROM PickupPoints ORDER BY PickupPointId", "Address", "PickupPointId"); if (IsEdit) { LoadOrder(); } } private void LoadOrder() { using var connection = Db.OpenConnection(); string sql = """ SELECT o.*, (SELECT TOP 1 Article FROM OrderProducts WHERE OrderId=o.OrderId) AS Article FROM Orders o WHERE o.OrderId=@id """; using var command = new SqlCommand(sql, connection); command.Parameters.AddWithValue("@id", orderId); using var reader = command.ExecuteReader(); if (!reader.Read()) { Close(); return; } articleComboBox.SelectedValue = reader["Article"].ToString() ?? ""; statusComboBox.SelectedValue = Convert.ToInt32(reader["OrderStatusId"]); pickupComboBox.SelectedValue = Convert.ToInt32(reader["PickupPointId"]); orderDatePicker.Value = Convert.ToDateTime(reader["OrderDate"]); deliveryDatePicker.Value = Convert.ToDateTime(reader["DeliveryDate"]); } private void saveButton_Click(object sender, EventArgs e) { int id = IsEdit ? orderId : NewOrderId(); string article = articleComboBox.SelectedValue?.ToString() ?? ""; using var connection = Db.OpenConnection(); string sql = IsEdit ? """ UPDATE Orders SET OrderDate=@orderDate, DeliveryDate=@deliveryDate, PickupPointId=@point, OrderStatusId=@status WHERE OrderId=@id; DELETE FROM OrderProducts WHERE OrderId=@id; INSERT INTO OrderProducts(OrderId, Article, Quantity) VALUES(@id, @article, 1); """ : """ INSERT INTO Orders(OrderId, OrderDate, DeliveryDate, PickupPointId, UserId, ReceiptCode, OrderStatusId) VALUES(@id, @orderDate, @deliveryDate, @point, NULL, @code, @status); INSERT INTO OrderProducts(OrderId, Article, Quantity) VALUES(@id, @article, 1); """; using var command = new SqlCommand(sql, connection); command.Parameters.AddWithValue("@id", id); command.Parameters.AddWithValue("@orderDate", orderDatePicker.Value.Date); command.Parameters.AddWithValue("@deliveryDate", deliveryDatePicker.Value.Date); command.Parameters.AddWithValue("@point", pickupComboBox.SelectedValue); command.Parameters.AddWithValue("@status", statusComboBox.SelectedValue); command.Parameters.AddWithValue("@article", article); command.Parameters.AddWithValue("@code", (900 + id).ToString()); command.ExecuteNonQuery(); Close(); } private int NewOrderId() { using var connection = Db.OpenConnection(); using var command = new SqlCommand("SELECT ISNULL(MAX(OrderId), 0) + 1 FROM Orders", connection); return Convert.ToInt32(command.ExecuteScalar()); } private void cancelButton_Click(object sender, EventArgs e) { Close(); } } } Что учить как шаблон Открыть подключение using var connection = Db.OpenConnection(); SELECT using var command = new SqlCommand(sql, connection); using var reader = command.ExecuteReader(); while (reader.Read()) { } INSERT / UPDATE / DELETE using var command = new SqlCommand(sql, connection); command.Parameters.AddWithValue("@id", id); command.ExecuteNonQuery(); ComboBox Db.LoadComboBox(comboBox, "SELECT Id, Title FROM Table", "Title", "Id"); Картинка Db.GetImage(fileName) Одна форма на добавление и редактирование new ProductEditForm("") // добавить товар new ProductEditForm(article) // редактировать товар new OrderEditForm(0) // добавить заказ new OrderEditForm(orderId) // редактировать заказ Главное правило проекта: Form1 -> вход ProductForm -> список товаров ProductCard -> карточка товара ProductEditForm -> товар OrdersForm -> список заказов OrderCard -> карточка заказа OrderEditForm -> заказ Db -> общие методы