2 лаба хард в процессе

This commit is contained in:
Полина Чубыкина 2024-04-23 22:23:21 +04:00
parent a58abc2610
commit 3c99a9a969
15 changed files with 545 additions and 7 deletions

View File

@ -17,5 +17,7 @@ namespace ConfectioneryContracts.BindingModels
get;
set;
} = new();
public int PastriesMaximum { get; set; }
}
}

View File

@ -18,5 +18,6 @@ namespace ConfectioneryContracts.BusinessLogicsContracts
bool Delete(ShopBindingModel model);
bool Supply(ShopSearchModel shopModel, IPastryModel pastry, int count);
bool Sale(IPastryModel model, int count);
}
}

View File

@ -1,6 +1,7 @@
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.ViewModels;
using ConfectioneryDataModels.Models;
namespace ConfectioneryContracts.StoragesContracts
{
@ -17,5 +18,6 @@ namespace ConfectioneryContracts.StoragesContracts
ShopViewModel? Update(ShopBindingModel model);
ShopViewModel? Delete(ShopBindingModel model);
bool Sale(IPastryModel model, int count);
}
}

View File

@ -21,5 +21,7 @@ namespace ConfectioneryContracts.ViewModels
get;
set;
} = new();
[DisplayName("Максимальное количество выпечки")]
public int PastriesMaximum { get; set; }
}
}

View File

@ -9,5 +9,6 @@
DateTime DateOpening { get; }
Dictionary<int, (IPastryModel, int)> ShopPastries { get; }
int PastriesMaximum { get; }
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfectioneryDataModels.Models;
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.ViewModels;
@ -18,11 +19,20 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
private readonly ILogger _logger;
private readonly IOrderStorage _orderStorage;
private readonly IPastryStorage _pastryStorage;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage)
private readonly IShopStorage _shopStorage;
private readonly IShopLogic _shopLogic;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, IPastryStorage pastryStorage,
IShopStorage shopStorage, IShopLogic shopLogic)
{
_logger = logger;
_orderStorage = orderStorage;
_pastryStorage = pastryStorage;
_shopStorage = shopStorage;
_shopLogic = shopLogic;
}
public bool CreateOrder(OrderBindingModel model)
@ -111,10 +121,20 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
newStatus, order.Status);
return false;
}
model.PastryId = order.PastryId;
model.Count = order.Count;
model.Sum = order.Sum;
model.DateCreate = order.DateCreate;
if (newStatus == OrderStatus.Выдан)
{
var pastry = _pastryStorage.GetElement(new PastrySearchModel() { Id = order.PastryId });
if (pastry == null)
{
_logger.LogWarning("Change status operation failed. Pastry not found");
return false;
}
if (!DeliverPastries(pastry, order.Count))
{
_logger.LogWarning("Change status operation failed. Pastries delivery operation failed");
return false;
}
}
model.Status = newStatus;
if (model.Status == OrderStatus.Готов)
{
@ -131,5 +151,63 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
}
return true;
}
private bool DeliverPastries(IPastryModel pastry, int count)
{
if (count <= 0)
{
_logger.LogWarning("Pastries delivery operation failed. Pastry count <= 0");
return false;
}
var shopList = _shopStorage.GetFullList();
int shopsCapacity = shopList.Sum(x => x.PastriesMaximum);
int currentPastrys = shopList.Select(x => x.ShopPastries.Sum(y => y.Value.Item2)).Sum();
int freePlaces = shopsCapacity - currentPastrys;
if (freePlaces < count)
{
_logger.LogWarning("Pastries delivery operation failed. No space for new pastries");
return false;
}
foreach (var shop in shopList)
{
freePlaces = shop.PastriesMaximum - shop.ShopPastries.Sum(x => x.Value.Item2);
if (freePlaces == 0)
{
continue;
}
if (freePlaces >= count)
{
if (_shopLogic.Supply(new() { Id = shop.Id }, pastry, count))
{
count = 0;
}
else
{
_logger.LogWarning("Pastries delivery operation failed");
return false;
}
}
else
{
if (_shopLogic.Supply(new() { Id = shop.Id }, pastry, freePlaces))
{
count -= freePlaces;
}
else
{
_logger.LogWarning("Pastries delivery operation failed");
return false;
}
}
if (count == 0)
{
return true;
}
}
return false;
}
}
}

View File

@ -105,6 +105,11 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
_logger.LogWarning("Supply(GetElement). Element not found");
return false;
}
if (shop.PastriesMaximum - shop.ShopPastries.Sum(x => x.Value.Item2) < count)
{
_logger.LogWarning("MakeShipment error. No space for pastries");
return false;
}
if (shop.ShopPastries.ContainsKey(Pastry.Id))
{
var shopP = shop.ShopPastries[Pastry.Id];
@ -125,6 +130,7 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
ShopName = shop.ShopName,
Address = shop.Address,
DateOpening = shop.DateOpening,
PastriesMaximum = shop.PastriesMaximum,
ShopPastries = shop.ShopPastries,
}) == null)
{
@ -134,6 +140,11 @@ namespace ConfectioneryBusinessLogic.BusinessLogics
return true;
}
public bool Sale(IPastryModel model, int count)
{
return _shopStorage.Sale(model, count);
}
private void CheckModel(ShopBindingModel model, bool withParams = true)
{
if (model == null)

View File

@ -15,9 +15,11 @@ namespace ConfectioneryFileImplement
private readonly string ComponentFileName = "Component.xml";
private readonly string OrderFileName = "Order.xml";
private readonly string PastryFileName = "Pastry.xml";
private readonly string ShopFileName = "Shop.xml";
public List<Component> Components { get; private set; }
public List<Order> Orders { get; private set; }
public List<Pastry> Pastries { get; private set; }
public List<Shop> Shops { get; private set; }
public static DataFileSingleton GetInstance()
{
if (instance == null)
@ -29,11 +31,13 @@ namespace ConfectioneryFileImplement
public void SaveComponents() => SaveData(Components, ComponentFileName, "Components", x => x.GetXElement);
public void SavePastries() => SaveData(Pastries, PastryFileName, "Pastries", 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 => Component.Create(x)!)!;
Pastries = LoadData(PastryFileName, "Pastry", x => Pastry.Create(x)!)!;
Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!;
Shops = LoadData(ShopFileName, "Shop", x => Shop.Create(x)!)!;
}
private static List<T>? LoadData<T>(string filename, string xmlNodeName,
Func<XElement, T> selectFunction)

View File

@ -0,0 +1,133 @@
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.StoragesContracts;
using ConfectioneryContracts.ViewModels;
using ConfectioneryFileImplement.Models;
using ConfectioneryDataModels.Models;
using System.Collections.Generic;
namespace ConfectioneryFileImplement.Implements
{
public class ShopStorage : IShopStorage
{
private readonly DataFileSingleton source;
public ShopStorage()
{
source = DataFileSingleton.GetInstance();
}
public List<ShopViewModel> GetFullList()
{
return source.Shops
.Select(x => x.GetViewModel)
.ToList();
}
public List<ShopViewModel> 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 element = source.Shops.FirstOrDefault(x => x.Id == model.Id);
if (element != null)
{
source.Shops.Remove(element);
source.SaveShops();
return element.GetViewModel;
}
return null;
}
public bool Sale(IPastryModel model, int count)
{
var pastry = source.Pastries.FirstOrDefault(x => x.Id == model.Id);
int countInShops = source.Shops.SelectMany(x => x.ShopPastries).Sum(y => y.Key == model.Id ? y.Value.Item2 : 0);
if (pastry == null || countInShops < count)
{
return false;
}
foreach (var shop in source.Shops)
{
var shopPastries = shop.ShopPastries.Where(x => x.Key == model.Id);
if (shopPastries.Any())
{
var shopPastry = shopPastries.First();
int min = Math.Min(shopPastry.Value.Item2, count);
if (min == shopPastry.Value.Item2)
{
shop.ShopPastries.Remove(shopPastry.Key);
}
else
{
shop.ShopPastries[shopPastry.Key] = (shopPastry.Value.Item1, shopPastry.Value.Item2 - min);
}
shop.Update(new ShopBindingModel
{
Id = shop.Id,
ShopName = shop.ShopName,
Address = shop.Address,
DateOpening = shop.DateOpening,
ShopPastries = shop.ShopPastries,
PastriesMaximum = shop.PastriesMaximum
});
count -= min;
if (count <= 0)
{
break;
}
}
}
source.SaveShops();
return true;
}
}
}

View File

@ -0,0 +1,113 @@
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.ViewModels;
using ConfectioneryDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConfectioneryFileImplement.Models
{
public class Shop
{
public int Id { get; private set; }
public string ShopName { get; private set; } = string.Empty;
public string Address { get; private set; } = string.Empty;
public DateTime DateOpening { get; private set; }
public Dictionary<int, int> Pastries { get; private set; } = new();
private Dictionary<int, (IPastryModel, int)>? _shopPastries = null;
public Dictionary<int, (IPastryModel, int)> ShopPastries
{
get
{
if (_shopPastries == null)
{
var source = DataFileSingleton.GetInstance();
_shopPastries = Pastries.ToDictionary(x => x.Key,
y => ((source.Pastries.FirstOrDefault(z => z.Id == y.Key) as IPastryModel)!, y.Value));
}
return _shopPastries;
}
}
public int PastriesMaximum { 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,
DateOpening = model.DateOpening,
Pastries = model.ShopPastries.ToDictionary(x => x.Key, x => x.Value.Item2),
PastriesMaximum = model.PastriesMaximum
};
}
public static Shop? Create(XElement element)
{
if (element == null)
{
return null;
}
return new Shop()
{
Id = Convert.ToInt32(element.Attribute("Id")!.Value),
ShopName = element.Element("ShopName")!.Value,
Address = element.Element("Address")!.Value,
DateOpening = Convert.ToDateTime(element.Element("DateOpening")!.Value),
PastriesMaximum = Convert.ToInt32(element.Element("PastriesMaximum")!.Value),
Pastries = element.Element("ShopPastries")!.Elements("ShopPastry")
.ToDictionary(x => Convert.ToInt32(x.Element("Key")?.Value), x => Convert.ToInt32(x.Element("Value")?.Value))
};
}
public void Update(ShopBindingModel? model)
{
if (model == null)
{
return;
}
ShopName = model.ShopName;
Address = model.Address;
DateOpening = model.DateOpening;
PastriesMaximum = model.PastriesMaximum;
Pastries = model.ShopPastries.ToDictionary(x => x.Key, x => x.Value.Item2);
_shopPastries = null;
}
public ShopViewModel GetViewModel => new()
{
Id = Id,
ShopName = ShopName,
Address = Address,
DateOpening = DateOpening,
ShopPastries = ShopPastries,
PastriesMaximum = PastriesMaximum
};
public XElement GetXElement => new("Shop",
new XAttribute("Id", Id),
new XElement("ShopName", ShopName),
new XElement("Address", Address),
new XElement("DateOpening", DateOpening.ToString()),
new XElement("PastriesMaximum", PastriesMaximum.ToString()),
new XElement("ShopPastries",
Pastries.Select(x => new XElement("ShopPastry",
new XElement("Key", x.Key),
new XElement("Value", x.Value))).ToArray()));
}
}

View File

@ -3,6 +3,7 @@ using ConfectioneryContracts.SearchModels;
using ConfectioneryContracts.StoragesContracts;
using ConfectioneryContracts.ViewModels;
using ConfectioneryListImplement.Models;
using ConfectioneryDataModels.Models;
namespace ConfectioneryListImplement.Implements
{
@ -105,5 +106,10 @@ namespace ConfectioneryListImplement.Implements
}
return null;
}
public bool Sale(IPastryModel model, int count)
{
throw new NotImplementedException();
}
}
}

View File

@ -1,6 +1,7 @@
using ConfectioneryContracts.BindingModels;
using ConfectioneryContracts.ViewModels;
using ConfectioneryDataModels.Models;
using System.Reflection;
namespace ConfectioneryListImplement.Models
{
@ -20,6 +21,8 @@ namespace ConfectioneryListImplement.Models
private set;
} = new Dictionary<int, (IPastryModel, int)>();
public int PastriesMaximum { get; private set; }
public static Shop? Create(ShopBindingModel? model)
{
if (model == null)
@ -32,7 +35,8 @@ namespace ConfectioneryListImplement.Models
ShopName = model.ShopName,
Address = model.Address,
DateOpening = model.DateOpening,
ShopPastries = model.ShopPastries
ShopPastries = model.ShopPastries,
PastriesMaximum = model.PastriesMaximum
};
}
@ -46,6 +50,7 @@ namespace ConfectioneryListImplement.Models
Address = model.Address;
DateOpening = model.DateOpening;
ShopPastries = model.ShopPastries;
PastriesMaximum = model.PastriesMaximum;
}
public ShopViewModel GetViewModel => new()
@ -54,7 +59,8 @@ namespace ConfectioneryListImplement.Models
ShopName = ShopName,
Address = Address,
DateOpening = DateOpening,
ShopPastries = ShopPastries
ShopPastries = ShopPastries,
PastriesMaximum = PastriesMaximum
};
}
}

View File

@ -0,0 +1,39 @@
namespace ConfectioneryView
{
partial class FormPastrySale
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Text = "FormPastrySale";
}
#endregion
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConfectioneryView
{
public partial class FormPastrySale : Form
{
public FormPastrySale()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>