using ChudoObuv.Logic;
using ChudoObuv.Models;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ChudoObuv.Data
{
///
/// Все запросы приложения к базе данных.
///
public static class DataService
{
// ---------------------------- Пользователи ----------------------------
public static User FindUserByLogin(string login)
{
const string sql = @"
SELECT u.user_id, u.last_name, u.first_name, u.patronymic, u.login, r.role_name
FROM users u
JOIN roles r ON r.role_id = u.role_id
WHERE u.login = @login";
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@login", login);
using MySqlDataReader reader = command.ExecuteReader();
return reader.Read() ? ReadUser(reader) : null;
}
public static List GetClients()
{
const string sql = @"
SELECT u.user_id, u.last_name, u.first_name, u.patronymic, u.login, r.role_name
FROM users u
JOIN roles r ON r.role_id = u.role_id
WHERE r.role_name = @role
ORDER BY u.last_name, u.first_name";
List clients = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@role", User.ClientRole);
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
clients.Add(ReadUser(reader));
}
return clients;
}
private static User ReadUser(MySqlDataReader reader)
{
return new User
{
Id = reader.GetInt32("user_id"),
LastName = reader.GetString("last_name"),
FirstName = reader.GetString("first_name"),
Patronymic = reader.IsDBNull(reader.GetOrdinal("patronymic")) ? "" : reader.GetString("patronymic"),
Login = reader.GetString("login"),
Role = reader.GetString("role_name")
};
}
// ------------------------------- Товары -------------------------------
public static List GetProducts()
{
const string sql = @"
SELECT p.product_id, p.product_name, c.category_name, s.subcategory_name,m.manufacture_name,p.description, p.composition, p.price, p.image_file,
COALESCE((SELECT SUM(si.quantity) FROM stock_items si WHERE si.product_id = p.product_id), 0) AS total_quantity,
EXISTS (SELECT 1 FROM order_items oi
JOIN orders o ON o.order_id = oi.order_id
JOIN stock_items si ON si.stock_item_id = oi.stock_item_id
WHERE si.product_id = p.product_id
AND o.order_date >= @previousMonthStart
AND o.order_date < @currentMonthStart) AS has_previous_month_orders
FROM products p
JOIN categories c ON c.category_id = p.category_id
JOIN subcategories s ON s.subcategory_id = p.subcategory_id
JOIN manufacturers m ON m.manufacture_id = p.manufacturer_id
ORDER BY p.product_id";
DateTime today = DateTime.Today;
List products = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@previousMonthStart", PriceCalculator.GetPreviousMonthStart(today));
command.Parameters.AddWithValue("@currentMonthStart", PriceCalculator.GetCurrentMonthStart(today));
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
products.Add(new Product
{
Id = reader.GetInt32("product_id"),
Name = reader.GetString("product_name"),
Category = reader.GetString("category_name"),
Subcategory = reader.GetString("subcategory_name"),
Manufacturer = reader.GetString("manufacture_name"),
Description = reader.IsDBNull(reader.GetOrdinal("description")) ? "" :
reader.GetString("description"),
Composition = reader.IsDBNull(reader.GetOrdinal("composition")) ? "" :
reader.GetString("composition"),
Price = reader.GetDecimal("price"),
ImageFile = reader.IsDBNull(reader.GetOrdinal("image_file")) ? null :
reader.GetString("image_file"),
TotalQuantity = Convert.ToInt32(reader["total_quantity"]),
HasOrdersInPreviousMonth = Convert.ToInt32(reader["has_previous_month_orders"]) == 1
});
}
return products;
}
public static List GetCategories()
{
List categories = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand("SELECT category_name FROM categories ORDER BY category_name", connection);
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
categories.Add(reader.GetString(0));
}
return categories;
}
///
/// Все товарные позиции модели (размерный ряд) с доступным количеством.
///
public static List GetStockItems(int productId)
{
const string sql = @"
SELECT si.stock_item_id, si.product_id, s.size_value, si.quantity
FROM stock_items si
JOIN sizes s ON s.size_id = si.size_id
WHERE si.product_id = @productId
ORDER BY s.size_value";
List items = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@productId", productId);
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
items.Add(new StockItem
{
Id = reader.GetInt32("stock_item_id"),
ProductId = reader.GetInt32("product_id"),
Size = reader.GetDecimal("size_value"),
Quantity = reader.GetInt32("quantity")
});
}
return items;
}
// ------------------------------- Заказы -------------------------------
public static List GetOrders()
{
const string sql = @"
SELECT o.order_id, o.order_date, u.user_id,
CONCAT_WS(' ', u.last_name, u.first_name, u.patronymic) AS client_name,
COUNT(oi.order_item_id) AS items_count,
COALESCE(SUM(oi.quantity * oi.unit_price), 0) AS total_sum
FROM orders o
JOIN users u ON u.user_id = o.user_id
LEFT JOIN order_items oi ON oi.order_id = o.order_id
GROUP BY o.order_id, o.order_date, u.user_id, client_name
ORDER BY o.order_date DESC, o.order_id DESC";
List orders = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
orders.Add(new Order
{
Id = reader.GetInt32("order_id"),
OrderDate = reader.GetDateTime("order_date"),
ClientId = reader.GetInt32("user_id"),
ClientFullName = reader.GetString("client_name"),
ItemsCount = Convert.ToInt32(reader["items_count"]),
TotalSum = reader.GetDecimal("total_sum")
});
}
return orders;
}
public static List GetOrderItems(int orderId)
{
const string sql = @"
SELECT oi.order_item_id, oi.stock_item_id, p.product_name, m.manufacturer_name, s.size_value, oi.quantity, oi.unit_price
FROM order_items oi
JOIN stock_items si ON si.stock_item_id = oi.stock_item_id
JOIN products p ON p.product_id = si.product_id
JOIN manufacturers m ON m.manufacturer_id = p.manufacturer_id
JOIN sizes s ON s.size_id = si.size_id
WHERE oi.order_id = @orderId
ORDER BY oi.order_item_id";
List items = new List();
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand(sql, connection);
command.Parameters.AddWithValue("@orderId", orderId);
using MySqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
items.Add(new OrderItem
{
Id = reader.GetInt32("order_item_id"),
StockItemId = reader.GetInt32("stock_item_id"),
ProductName = reader.GetString("product_name"),
Manufacturer = reader.GetString("manufacturer_name"),
Size = reader.GetDecimal("size_value"),
Quantity = reader.GetInt32("quantity"),
UnitPrice = reader.GetDecimal("unit_price")
});
}
return items;
}
///
/// Сохраняет заказ и уменьшает остатки. Возвращает номер заказа.
/// Если какого-то размера уже не хватает — заказ не сохраняется (исключение).
///
public static int CreateOrder(int clientId, List items)
{
using MySqlConnection connection = Database.OpenConnection();
using MySqlTransaction transaction = connection.BeginTransaction();
MySqlCommand orderCommand = new MySqlCommand(
"INSERT INTO orders (order_date, user_id) VALUES (@date, @userId)", connection,
transaction);
orderCommand.Parameters.AddWithValue("@date", DateTime.Today);
orderCommand.Parameters.AddWithValue("@userId", clientId);
orderCommand.ExecuteNonQuery();
int orderId = (int)orderCommand.LastInsertedId;
foreach (OrderItem item in items)
{
// Остаток уменьшается только если его хватает — защита от одновременных заказов
MySqlCommand stockCommand = new MySqlCommand(
"UPDATE stock_items SET quantity = quantity - @quantity WHERE stock_item_id = @id AND quantity >= @quantity",
connection, transaction);
stockCommand.Parameters.AddWithValue("@quantity", item.Quantity);
stockCommand.Parameters.AddWithValue("@id", item.StockItemId);
if (stockCommand.ExecuteNonQuery() == 0)
{
transaction.Rollback();
throw new InvalidOperationException(
$"Недостаточно товара «{item.ProductName}», размер {item.SizeText}. Обновите каталог и измените количество.");
}
MySqlCommand itemCommand = new MySqlCommand(
"INSERT INTO order_items (order_id, stock_item_id, quantity, unit_price) VALUES (@orderId, @stockItemId, @quantity, @price)",
connection, transaction);
itemCommand.Parameters.AddWithValue("@orderId", orderId);
itemCommand.Parameters.AddWithValue("@stockItemId", item.StockItemId);
itemCommand.Parameters.AddWithValue("@quantity", item.Quantity);
itemCommand.Parameters.AddWithValue("@price", item.UnitPrice);
itemCommand.ExecuteNonQuery();
}
transaction.Commit();
return orderId;
}
///
/// Удаляет заказ и возвращает его товары на склад.
///
public static void DeleteOrder(int orderId)
{
using MySqlConnection connection = Database.OpenConnection();
using MySqlTransaction transaction = connection.BeginTransaction();
MySqlCommand returnCommand = new MySqlCommand(@"
UPDATE stock_items si
JOIN order_items oi ON oi.stock_item_id = si.stock_item_id
SET si.quantity = si.quantity + oi.quantity
WHERE oi.order_id = @orderId", connection, transaction);
returnCommand.Parameters.AddWithValue("@orderId", orderId);
returnCommand.ExecuteNonQuery();
// Строки состава удаляются каскадно (ON DELETE CASCADE)
MySqlCommand deleteCommand = new MySqlCommand("DELETE FROM orders WHERE order_id = @orderId", connection, transaction);
deleteCommand.Parameters.AddWithValue("@orderId", orderId);
deleteCommand.ExecuteNonQuery();
transaction.Commit();
}
///
/// Удаляет товарную позицию из заказа и возвращает ее количество на склад.
///
public static void DeleteOrderItem(int orderItemId)
{
using MySqlConnection connection = Database.OpenConnection();
using MySqlTransaction transaction = connection.BeginTransaction();
MySqlCommand returnCommand = new MySqlCommand(@"
UPDATE stock_items si
JOIN order_items oi ON oi.stock_item_id = si.stock_item_id
SET si.quantity = si.quantity + oi.quantity
WHERE oi.order_item_id = @id", connection, transaction);
returnCommand.Parameters.AddWithValue("@id", orderItemId);
returnCommand.ExecuteNonQuery();
MySqlCommand deleteCommand = new MySqlCommand("DELETE FROM order_items WHERE order_item_id = @id", connection, transaction);
deleteCommand.Parameters.AddWithValue("@id", orderItemId);
deleteCommand.ExecuteNonQuery();
transaction.Commit();
}
public static void UpdateOrderDate(int orderId, DateTime orderDate)
{
using MySqlConnection connection = Database.OpenConnection();
using MySqlCommand command = new MySqlCommand("UPDATE orders SET order_date = @date WHERE order_id = @orderId", connection);
command.Parameters.AddWithValue("@date", orderDate.Date);
command.Parameters.AddWithValue("@orderId", orderId);
command.ExecuteNonQuery();
}
}
}