diff --git a/CarpentryWorkshop/CarpentryWorkshopBusinessLogic/BusinessLogics/OrderLogic.cs b/CarpentryWorkshop/CarpentryWorkshopBusinessLogic/BusinessLogics/OrderLogic.cs index 66a3eee..9ac7bf2 100644 --- a/CarpentryWorkshop/CarpentryWorkshopBusinessLogic/BusinessLogics/OrderLogic.cs +++ b/CarpentryWorkshop/CarpentryWorkshopBusinessLogic/BusinessLogics/OrderLogic.cs @@ -12,11 +12,13 @@ namespace CarpentryWorkshopBusinessLogic.BusinessLogics { private readonly ILogger _logger; private readonly IOrderStorage _orderStorage; + private readonly IShopStorage _shopStorage; - public OrderLogic(ILogger logger, IOrderStorage orderStorage) + public OrderLogic(ILogger logger, IOrderStorage orderStorage, IShopStorage shopStorage) { _logger = logger; _orderStorage = orderStorage; + _shopStorage = shopStorage; } public List? ReadList(OrderSearchModel? model) @@ -78,6 +80,22 @@ namespace CarpentryWorkshopBusinessLogic.BusinessLogics public bool DeliveryOrder(OrderBindingModel model) { + var order = _orderStorage.GetElement(new OrderSearchModel + { + Id = model.Id, + }); + if (order == null) + { + throw new ArgumentNullException(nameof(order)); + } + if (!_shopStorage.RestockingShops(new SupplyBindingModel + { + WoodId = order.WoodId, + Count = order.Count + })) + { + throw new ArgumentException("Недостаточно места"); + } return ChangeStatus(model, OrderStatus.Выдан); } diff --git a/CarpentryWorkshop/CarpentryWorkshopContracts/BindingModels/ShopBindingModel.cs b/CarpentryWorkshop/CarpentryWorkshopContracts/BindingModels/ShopBindingModel.cs index feed4c9..1ca3279 100644 --- a/CarpentryWorkshop/CarpentryWorkshopContracts/BindingModels/ShopBindingModel.cs +++ b/CarpentryWorkshop/CarpentryWorkshopContracts/BindingModels/ShopBindingModel.cs @@ -9,5 +9,6 @@ namespace CarpentryWorkshopContracts.BindingModels public string Address { get; set; } public DateTime DateOpen { get; set; } public Dictionary ShopWoods { get; set; } = new(); + public int WoodMaxCount { get; set; } } } diff --git a/CarpentryWorkshop/CarpentryWorkshopContracts/BusinessLogicsContracts/IShopLogic.cs b/CarpentryWorkshop/CarpentryWorkshopContracts/BusinessLogicsContracts/IShopLogic.cs index 5840f0f..97fbb5a 100644 --- a/CarpentryWorkshop/CarpentryWorkshopContracts/BusinessLogicsContracts/IShopLogic.cs +++ b/CarpentryWorkshop/CarpentryWorkshopContracts/BusinessLogicsContracts/IShopLogic.cs @@ -13,5 +13,6 @@ namespace CarpentryWorkshopContracts.BusinessLogicsContracts bool Update(ShopBindingModel model); bool Delete(ShopBindingModel model); bool MakeSupply(SupplyBindingModel model); + bool Sale(SupplySearchModel model); } } diff --git a/CarpentryWorkshop/CarpentryWorkshopContracts/SearchModels/SupplySearchModel.cs b/CarpentryWorkshop/CarpentryWorkshopContracts/SearchModels/SupplySearchModel.cs new file mode 100644 index 0000000..27b4eeb --- /dev/null +++ b/CarpentryWorkshop/CarpentryWorkshopContracts/SearchModels/SupplySearchModel.cs @@ -0,0 +1,8 @@ +namespace CarpentryWorkshopContracts.SearchModels +{ + public class SupplySearchModel + { + public int? WoodId { get; set; } + public int? Count { get; set; } + } +} diff --git a/CarpentryWorkshop/CarpentryWorkshopContracts/StoragesContracts/IShopStorage.cs b/CarpentryWorkshop/CarpentryWorkshopContracts/StoragesContracts/IShopStorage.cs index 465cef8..3638058 100644 --- a/CarpentryWorkshop/CarpentryWorkshopContracts/StoragesContracts/IShopStorage.cs +++ b/CarpentryWorkshop/CarpentryWorkshopContracts/StoragesContracts/IShopStorage.cs @@ -18,5 +18,7 @@ namespace CarpentryWorkshopContracts.StoragesContracts ShopViewModel? Insert(ShopBindingModel model); ShopViewModel? Update(ShopBindingModel model); ShopViewModel? Delete(ShopBindingModel model); + bool Sale(SupplySearchModel model); + bool RestockingShops(SupplyBindingModel model); } } diff --git a/CarpentryWorkshop/CarpentryWorkshopContracts/ViewModels/ShopViewModel.cs b/CarpentryWorkshop/CarpentryWorkshopContracts/ViewModels/ShopViewModel.cs index d469d57..ec8eed1 100644 --- a/CarpentryWorkshop/CarpentryWorkshopContracts/ViewModels/ShopViewModel.cs +++ b/CarpentryWorkshop/CarpentryWorkshopContracts/ViewModels/ShopViewModel.cs @@ -13,5 +13,7 @@ namespace CarpentryWorkshopContracts.ViewModels [DisplayName("Дата открытия")] public DateTime DateOpen { get; set; } public Dictionary ShopWoods { get; set; } = new(); + [DisplayName("Вместимость")] + public int WoodMaxCount { get; set; } } } diff --git a/CarpentryWorkshop/CarpentryWorkshopDataModels/Models/IShopModel.cs b/CarpentryWorkshop/CarpentryWorkshopDataModels/Models/IShopModel.cs index 19e6d4c..b6a8824 100644 --- a/CarpentryWorkshop/CarpentryWorkshopDataModels/Models/IShopModel.cs +++ b/CarpentryWorkshop/CarpentryWorkshopDataModels/Models/IShopModel.cs @@ -6,5 +6,6 @@ string Address { get; } DateTime DateOpen { get; } Dictionary ShopWoods { get; } + public int WoodMaxCount { get; } } } diff --git a/CarpentryWorkshop/CarpentryWorkshopFileImplement/DataFileSingleton.cs b/CarpentryWorkshop/CarpentryWorkshopFileImplement/DataFileSingleton.cs index 3d9819c..87c458e 100644 --- a/CarpentryWorkshop/CarpentryWorkshopFileImplement/DataFileSingleton.cs +++ b/CarpentryWorkshop/CarpentryWorkshopFileImplement/DataFileSingleton.cs @@ -9,9 +9,11 @@ namespace CarpentryWorkshopFileImplement private readonly string ComponentFileName = "Component.xml"; private readonly string OrderFileName = "Order.xml"; private readonly string WoodFileName = "Wood.xml"; + private readonly string ShopFileName = "Shop.xml"; public List Components { get; private set; } public List Orders { get; private set; } public List Woods { get; private set; } + public List Shops { get; private set; } public static DataFileSingleton GetInstance() { if (instance == null) @@ -26,6 +28,8 @@ namespace CarpentryWorkshopFileImplement "Woods", x => x.GetXElement); public void SaveOrders() => SaveData(Orders, OrderFileName, "Orders", x => x.GetXElement); + public void SaveShops() => SaveData(Shops, ShopFileName, + "Shops", x => x.GetXElement); private DataFileSingleton() { Components = LoadData(ComponentFileName, "Component", x => @@ -34,6 +38,8 @@ namespace CarpentryWorkshopFileImplement Wood.Create(x)!)!; Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!; + Shops = LoadData(ShopFileName, "Shop", x => + Shop.Create(x)!)!; } private static List? LoadData(string filename, string xmlNodeName, Func selectFunction) diff --git a/CarpentryWorkshop/CarpentryWorkshopFileImplement/Implements/ShopStorage.cs b/CarpentryWorkshop/CarpentryWorkshopFileImplement/Implements/ShopStorage.cs new file mode 100644 index 0000000..dddfeb2 --- /dev/null +++ b/CarpentryWorkshop/CarpentryWorkshopFileImplement/Implements/ShopStorage.cs @@ -0,0 +1,148 @@ +using CarpentryWorkshopContracts.BindingModels; +using CarpentryWorkshopContracts.SearchModels; +using CarpentryWorkshopContracts.StoragesContracts; +using CarpentryWorkshopContracts.ViewModels; +using CarpentryWorkshopFileImplement.Models; + +namespace CarpentryWorkshopFileImplement.Implements +{ + public class ShopStorage : IShopStorage + { + private readonly DataFileSingleton source; + + public ShopStorage() + { + source = DataFileSingleton.GetInstance(); + } + + public List GetFullList() + { + return source.Shops.Select(x => x.GetViewModel).ToList(); + } + + public List GetFilteredList(ShopSearchModel model) + { + if (string.IsNullOrEmpty(model.ShopName)) + { + return new(); + } + return source.Shops.Where(x => x.ShopName.Contains(model.ShopName)).Select(x => x.GetViewModel).ToList(); + } + public ShopViewModel? GetElement(ShopSearchModel model) + { + if (string.IsNullOrEmpty(model.ShopName) && !model.Id.HasValue) + { + return null; + } + return source.Shops.FirstOrDefault(x => + (!string.IsNullOrEmpty(model.ShopName) && x.ShopName == model.ShopName) || + (model.Id.HasValue && x.Id == model.Id))?.GetViewModel; + } + + public ShopViewModel? Insert(ShopBindingModel model) + { + model.Id = source.Shops.Count > 0 ? source.Shops.Max(x => x.Id) + 1 : 1; + var newShop = Shop.Create(model); + if (newShop == null) + { + return null; + } + source.Shops.Add(newShop); + source.SaveShops(); + return newShop.GetViewModel; + } + + public ShopViewModel? Update(ShopBindingModel model) + { + var shop = source.Shops.FirstOrDefault(x => x.Id == model.Id); + if (shop == null) + { + return null; + } + shop.Update(model); + source.SaveShops(); + return shop.GetViewModel; + } + + public ShopViewModel? Delete(ShopBindingModel model) + { + var shop = source.Shops.FirstOrDefault(x => x.Id == model.Id); + if (shop != null) + { + source.Shops.Remove(shop); + source.SaveShops(); + return shop.GetViewModel; + } + return null; + } + + public bool Sale(SupplySearchModel model) + { + if (model == null || !model.WoodId.HasValue || !model.Count.HasValue) + return false; + int remainingSpace = source.Shops.Select(x => x.Woods.ContainsKey(model.WoodId.Value) ? x.Woods[model.WoodId.Value] : 0).Sum(); + if (remainingSpace < model.Count) + { + return false; + } + var shops = source.Shops.Where(x => x.Woods.ContainsKey(model.WoodId.Value)).OrderByDescending(x => x.Woods[model.WoodId.Value]).ToList(); + foreach (var shop in shops) + { + int residue = model.Count.Value - shop.Woods[model.WoodId.Value]; + if (residue > 0) + { + shop.Woods.Remove(model.WoodId.Value); + shop.Woods.Update(); + model.Count = residue; + } + else + { + if (residue == 0) + { + shop.Woods.Remove(model.WoodId.Value); + } + else + { + shop.Woods[model.WoodId.Value] = -residue; + } + shop.Woods.Update(); + source.SaveShops(); + return true; + } + } + source.SaveShops(); + return false; + } + + public bool RestockingShops(SupplyBindingModel model) + { + if (model == null || source.Shops.Select(x => x.WoodMaxCount - x.ShopWoods.Select(y => y.Value.Item2).Sum()).Sum() < model.Count) + { + return false; + } + foreach (Shop shop in source.Shops) + { + int free_places = shop.WoodMaxCount - shop.ShopWoods.Select(x => x.Value.Item2).Sum(); + if (free_places <= 0) + continue; + free_places = Math.Min(free_places, model.Count); + model.Count -= free_places; + if (shop.Woods.ContainsKey(model.WoodId)) + { + shop.Woods[model.WoodId] += free_places; + } + else + { + shop.Woods.Add(model.WoodId, free_places); + } + shop.Woods.Update(); + if (model.Count == 0) + { + source.SaveShops(); + return true; + } + } + return false; + } + } +} diff --git a/CarpentryWorkshop/CarpentryWorkshopFileImplement/Models/Shop.cs b/CarpentryWorkshop/CarpentryWorkshopFileImplement/Models/Shop.cs new file mode 100644 index 0000000..cf1c4ae --- /dev/null +++ b/CarpentryWorkshop/CarpentryWorkshopFileImplement/Models/Shop.cs @@ -0,0 +1,101 @@ +using CarpentryWorkshopContracts.BindingModels; +using CarpentryWorkshopContracts.ViewModels; +using CarpentryWorkshopDataModels.Models; +using System.Xml.Linq; + +namespace CarpentryWorkshopFileImplement.Models +{ + public class Shop : IShopModel + { + public int Id { get; private set; } + public string ShopName { get; private set; } = string.Empty; + public string Address { get; private set; } = string.Empty; + public DateTime DateOpen { get; private set; } + public Dictionary Woods { get; private set; } = new(); + private Dictionary? _shopWoods = null; + + public Dictionary ShopWoods + { + get + { + if (_shopWoods == null) + { + var source = DataFileSingleton.GetInstance(); + _shopWoods = Woods.ToDictionary(x => x.Key, y => ((source.Woods.FirstOrDefault(z => z.Id == y.Key) as IWoodModel)!, y.Value)); + } + return _shopWoods; + } + } + + public int WoodMaxCount { get; private set; } + + public static Shop? Create(ShopBindingModel? model) + { + if (model == null) + { + return null; + } + return new Shop() + { + Id = model.Id, + ShopName = model.ShopName, + Address = model.Address, + DateOpen = model.DateOpen, + Woods = model.ShopWoods.ToDictionary(x => x.Key, x => x.Value.Item2), + WoodMaxCount = model.WoodMaxCount + }; + } + + public static Shop? Create(XElement element) + { + if (element == null) + { + return null; + } + return new() + { + Id = Convert.ToInt32(element.Attribute("Id")!.Value), + ShopName = element.Element("ShopName")!.Value, + Address = element.Element("Address")!.Value, + DateOpen = Convert.ToDateTime(element.Element("DateOpen")!.Value), + Woods = element.Element("ShopWoods")!.Elements("ShopWood")!.ToDictionary(x => Convert.ToInt32(x.Element("Key")?.Value), + x => Convert.ToInt32(x.Element("Value")?.Value)), + WoodMaxCount = Convert.ToInt32(element.Element("WoodMaxCount")!.Value) + }; + } + + public void Update(ShopBindingModel? model) + { + if (model == null) + { + return; + } + ShopName = model.ShopName; + Address = model.Address; + DateOpen = model.DateOpen; + WoodMaxCount = model.WoodMaxCount; + Woods = model.ShopWoods.ToDictionary(x => x.Key, x => x.Value.Item2); + _shopWoods = null; + } + + public ShopViewModel GetViewModel => new() + { + Id = Id, + ShopName = ShopName, + Address = Address, + DateOpen = DateOpen, + ShopWoods = ShopWoods, + WoodMaxCount = WoodMaxCount + }; + + public XElement GetXElement => new("Shop", + new XAttribute("Id", Id), + new XElement("ShopName", ShopName), + new XElement("Address", Address), + new XElement("DateOpen", DateOpen.ToString()), + new XElement("ShopWoods", Woods.Select( + x => new XElement("ShopWood", new XElement("Key", x.Key), new XElement("Value", x.Value))).ToArray()), + new XElement("WoodMaxCount", WoodMaxCount.ToString()) + ); + } +}