diff --git a/BusinessLogic/BusinessLogic/ProductLogic.cs b/BusinessLogic/BusinessLogic/ProductLogic.cs new file mode 100644 index 0000000..e10985d --- /dev/null +++ b/BusinessLogic/BusinessLogic/ProductLogic.cs @@ -0,0 +1,125 @@ +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.BusinessLogic +{ + public class ProductLogic : IProductLogic + { + private readonly IProductStorage _productStorage; + private readonly ILogger _logger; + public ProductLogic(IProductStorage productStorage, ILogger logger) + { + _productStorage = productStorage; + _logger = logger; + } + public bool Create(ProductBindingModel model) + { + CheckModel(model); + if (_productStorage.Insert(model) == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + return true; + } + + public bool Delete(ProductBindingModel model) + { + CheckModel(model, false); + _logger.LogInformation("Delete. Id:{Id}", model.Id); + if (_productStorage.Delete(model) == null) + { + _logger.LogWarning("Delete operation failed"); + return false; + } + return true; + } + + public ProductViewModel? ReadElement(ProductSearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + _logger.LogInformation("ReadElement. Name:{Name}.Id:{ Id}", model.Name, model.Id); + var element = _productStorage.GetElement(model); + if (element == null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id:{Id}", element.Id); + return element; + } + + public List? ReadList(ProductSearchModel? model) + { + _logger.LogInformation("ReadList. Name:{Name}.Id:{ Id}", model?.Name, model?.Id); + var list = model == null ? _productStorage.GetFullList() : _productStorage.GetFilteredList(model); + if (list == null) + { + _logger.LogWarning("ReadList return null list"); + return null; + } + _logger.LogInformation("ReadList. Count:{Count}", list.Count); + return list; + } + + public bool Update(ProductBindingModel model) + { + CheckModel(model); + if (_productStorage.Update(model) == null) + { + _logger.LogWarning("Update operation failed"); + return false; + } + return true; + } + + private void CheckModel(ProductBindingModel model, bool withParams = true) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.Name)) + { + throw new ArgumentNullException("Нет названия продукта", nameof(model.Name)); + } + if (model.Price < 0) + { + throw new Exception("Цена продукта не может быть ниже нуля"); + } + if (model.Amount < 0) + { + throw new Exception("Кол-во продуктов не может быть ниже нуля"); + } + if (model.Rate < 0.0 || model.Rate > 5.0) + { + throw new Exception("Рейтинг продукта должен быть в пределах от 0.0 до 5.0"); + } + _logger.LogInformation("Product. ProductName:{Name}.Price:{ Price}. Id: { Id}", model.Name, model.Price, model.Id); + var element = _productStorage.GetElement(new ProductSearchModel + { + Name = model.Name + }); + if (element != null && element.Id != model.Id) + { + throw new InvalidOperationException("Продукт с таким названием уже есть"); + } + } + } +} diff --git a/BusinessLogic/BusinessLogic/SupplierLogic.cs b/BusinessLogic/BusinessLogic/SupplierLogic.cs new file mode 100644 index 0000000..e71654e --- /dev/null +++ b/BusinessLogic/BusinessLogic/SupplierLogic.cs @@ -0,0 +1,114 @@ +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.BusinessLogic +{ + public class SupplierLogic : ISupplierLogic + { + private readonly ISupplierStorage _supplierStorage; + private readonly ILogger _logger; + public SupplierLogic(ISupplierStorage supplierStorage, ILogger logger) + { + _supplierStorage = supplierStorage; + _logger = logger; + } + public bool Create(SupplierBindingModel model) + { + CheckModel(model); + if (_supplierStorage.Insert(model) == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + return true; + } + + public bool Delete(SupplierBindingModel model) + { + CheckModel(model, false); + _logger.LogInformation("Delete. Id:{Id}", model.Id); + if (_supplierStorage.Delete(model) == null) + { + _logger.LogWarning("Delete operation failed"); + return false; + } + return true; + } + + public SupplierViewModel? ReadElement(SupplierSearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + _logger.LogInformation("ReadElement. Name:{Name}.Id:{ Id}", model.Name, model.Id); + var element = _supplierStorage.GetElement(model); + if (element == null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id:{Id}", element.Id); + return element; + } + + public List? ReadList(SupplierSearchModel? model) + { + _logger.LogInformation("ReadList. Name:{Name}.Id:{ Id}", model?.Name, model?.Id); + var list = model == null ? _supplierStorage.GetFullList() : _supplierStorage.GetFilteredList(model); + if (list == null) + { + _logger.LogWarning("ReadList return null list"); + return null; + } + _logger.LogInformation("ReadList. Count:{Count}", list.Count); + return list; + } + + public bool Update(SupplierBindingModel model) + { + CheckModel(model); + if (_supplierStorage.Update(model) == null) + { + _logger.LogWarning("Update operation failed"); + return false; + } + return true; + } + private void CheckModel(SupplierBindingModel model, bool withParams = true) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.Name)) + { + throw new ArgumentNullException("Нет названия поставщика", nameof(model.Name)); + } + if (model.Deals < 0) + { + throw new Exception("Кол-во сделок не может быть меньше нуля"); + } + foreach(var item in model.AvailibleProducts.Values) + { + if (item.Item2 < 0) + { + throw new Exception("Кол-во доступных продуктов не должно быть отрицательным"); + } + } + } + } +} diff --git a/BusinessLogic/BusinessLogic/SupplyLogic.cs b/BusinessLogic/BusinessLogic/SupplyLogic.cs new file mode 100644 index 0000000..63a40d9 --- /dev/null +++ b/BusinessLogic/BusinessLogic/SupplyLogic.cs @@ -0,0 +1,105 @@ +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.BusinessLogic +{ + public class SupplyLogic : ISupplyLogic + { + private readonly ISupplyStorage _supplyStorage; + private readonly ILogger _logger; + public SupplyLogic(ISupplyStorage supplyStorage, ILogger logger) + { + _supplyStorage = supplyStorage; + _logger = logger; + } + + public bool Create(SupplyBindingModel model) + { + CheckModel(model); + if (_supplyStorage.Insert(model) == null) + { + _logger.LogWarning("Insert operation failed"); + return false; + } + return true; + } + + public bool Delete(SupplyBindingModel model) + { + CheckModel(model, false); + _logger.LogInformation("Delete. Id:{Id}", model.Id); + if (_supplyStorage.Delete(model) == null) + { + _logger.LogWarning("Delete operation failed"); + return false; + } + return true; + } + + public SupplyViewModel? ReadElement(SupplySearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + _logger.LogInformation("ReadElement. Name:{Name}.Id:{ Id}", model.Name, model.Id); + var element = _supplyStorage.GetElement(model); + if (element == null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id:{Id}", element.Id); + return element; + } + + public List? ReadList(SupplySearchModel? model) + { + _logger.LogInformation("ReadList. Name:{Name}.Id:{ Id}", model?.Name, model?.Id); + var list = model == null ? _supplyStorage.GetFullList() : _supplyStorage.GetFilteredList(model); + if (list == null) + { + _logger.LogWarning("ReadList return null list"); + return null; + } + _logger.LogInformation("ReadList. Count:{Count}", list.Count); + return list; + } + + public bool Update(SupplyBindingModel model) + { + CheckModel(model); + if (_supplyStorage.Update(model) == null) + { + _logger.LogWarning("Update operation failed"); + return false; + } + return true; + } + + private void CheckModel(SupplyBindingModel model, bool withParams = true) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model)); + } + if (!withParams) + { + return; + } + if (string.IsNullOrEmpty(model.Name)) + { + throw new ArgumentNullException("Нет названия поставки", nameof(model.Name)); + } + } + } +} diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index ee6b806..0ccec27 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -25,11 +25,14 @@ namespace BusinessLogic.BusinessLogic { private readonly ILogger _logger; private readonly IUserStorage _userStorage; + private readonly ITwoFactorAuthService _twoFactorAuthService; - public UserLogic(ILogger logger, IUserStorage userStorage) + public UserLogic(ILogger logger, IUserStorage userStorage, + ITwoFactorAuthService twoFactorAuthService) { _logger = logger; _userStorage = userStorage; + _twoFactorAuthService = twoFactorAuthService; } public string Create(UserBindingModel model) @@ -53,6 +56,9 @@ namespace BusinessLogic.BusinessLogic MailSender.Send(new MailRegistration(user)); + string code = _twoFactorAuthService.GenerateCode(); + MailSender.Send(new MailTwoFactorCode(user, code)); + return JwtProvider.Generate(user); } @@ -132,6 +138,10 @@ namespace BusinessLogic.BusinessLogic { throw new AccountException("The passwords don't match."); } + + string code = _twoFactorAuthService.GenerateCode(); + MailSender.Send(new MailTwoFactorCode(user, code)); + return JwtProvider.Generate(user); } @@ -166,5 +176,10 @@ namespace BusinessLogic.BusinessLogic throw new AccountException("The email is not valid."); } } + + public bool VerifyCode(string code) + { + return _twoFactorAuthService.Verify(code); + } } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/Mail.cs b/BusinessLogic/Tools/Mail/Mail.cs index 5adba23..44cc1a8 100644 --- a/BusinessLogic/Tools/Mail/Mail.cs +++ b/BusinessLogic/Tools/Mail/Mail.cs @@ -6,10 +6,11 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail { - public class Mail - { - public IEnumerable To { get; set; } = null!; - public string Title { get; set; } = null!; - public string Body { get; set; } = null!; - } + public class Mail + { + public IEnumerable To { get; set; } = null!; + public string Title { get; set; } = null!; + public string Body { get; set; } = null!; + public bool IsSendable { get; set; } = true; + } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailSender.cs b/BusinessLogic/Tools/Mail/MailSender.cs index 329b392..3df9c48 100644 --- a/BusinessLogic/Tools/Mail/MailSender.cs +++ b/BusinessLogic/Tools/Mail/MailSender.cs @@ -8,39 +8,41 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail { - public class MailSender - { - private static string _email; - private static string _password; - private static string _smtpClientHost; - private static short _smtpClientPort; + public class MailSender + { + private static string _email; + private static string _password; + private static string _smtpClientHost; + private static short _smtpClientPort; - public void SetupMailOptions(MailOptions options) - { - _email = options.Email; - _password = options.Password; - _smtpClientHost = options.SmtpClientHost; - _smtpClientPort = options.SmtpClientPort; - } + public void SetupMailOptions(MailOptions options) + { + _email = options.Email; + _password = options.Password; + _smtpClientHost = options.SmtpClientHost; + _smtpClientPort = options.SmtpClientPort; + } - public static void Send(Mail mail) - { - using SmtpClient client = new SmtpClient(_smtpClientHost, _smtpClientPort); - client.Credentials = new NetworkCredential(_email, _password); - client.EnableSsl = true; + public static void Send(Mail mail) + { + if (!mail.IsSendable) return; - using MailMessage message = new MailMessage(); + using SmtpClient client = new SmtpClient(_smtpClientHost, _smtpClientPort); + client.Credentials = new NetworkCredential(_email, _password); + client.EnableSsl = true; - message.From = new MailAddress(_email); - foreach (string to in mail.To) - { - message.To.Add(to); - } + using MailMessage message = new MailMessage(); - message.Subject = mail.Title; - message.Body = mail.Body; + message.From = new MailAddress(_email); + foreach (string to in mail.To) + { + message.To.Add(to); + } - client.Send(message); - } - } + message.Subject = mail.Title; + message.Body = mail.Body; + + client.Send(message); + } + } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs index fdd33d9..3a6c2dc 100644 --- a/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailRegistration.cs @@ -8,14 +8,18 @@ using System.Threading.Tasks; namespace BusinessLogic.Tools.Mail.MailTemplates { - public class MailRegistration : Mail - { - public MailRegistration(UserBindingModel user) - { - To = [user.Email]; - Title = "Приветствуем Вас на нашем сайте!"; - Body = $"Спасибо, {user.SecondName} {user.FirstName}, что выбрали НАС.\n" + - $"Надеемся, что Вам что-то уже приглянулось!"; - } - } + public class MailRegistration : Mail + { + public MailRegistration(UserBindingModel user) + { + if (user.OnlyImportantMails) + { + IsSendable = false; + } + To = [user.Email]; + Title = "Приветствуем Вас на нашем сайте!"; + Body = $"Спасибо, {user.SecondName} {user.FirstName}, что выбрали НАС.\n" + + $"Надеемся, что Вам что-то уже приглянулось!"; + } + } } \ No newline at end of file diff --git a/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs b/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs new file mode 100644 index 0000000..6c5e4d4 --- /dev/null +++ b/BusinessLogic/Tools/Mail/MailTemplates/MailTwoFactorCode.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools.Mail.MailTemplates +{ + public class MailTwoFactorCode : Mail + { + public MailTwoFactorCode(UserBindingModel user, string code) + { + To = [user.Email]; + Title = "Ваш код для подтверждения"; + Body = $"Здравствуйте, {user.SecondName} {user.FirstName}! Вот Ваш код для подтверждения:\n" + + $"{code}\n" + + $"Если это не Вы, игноритруйте это сообщение."; + } + } +} \ No newline at end of file diff --git a/BusinessLogic/Tools/TwoFactorAuthService.cs b/BusinessLogic/Tools/TwoFactorAuthService.cs new file mode 100644 index 0000000..5ab72d6 --- /dev/null +++ b/BusinessLogic/Tools/TwoFactorAuthService.cs @@ -0,0 +1,42 @@ +using Contracts.BusinessLogicContracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools +{ + public class TwoFactorAuthService : ITwoFactorAuthService + { + private string _code = string.Empty; + private const int LENGTH_CODE = 5; + + public string GenerateCode() + { + _code = _getCode(LENGTH_CODE); + return _code; + } + + private string _getCode(int length) + { + string res = ""; + var rand = new Random(); + for (int i = 0; i < length; i++) + { + res += rand.Next(0, 9).ToString(); + } + return res; + } + + public bool Verify(string code) + { + if (_code == string.Empty) + { + throw new Exception("Source code is not generated."); + } + + return _code == code; + } + } +} \ No newline at end of file diff --git a/Contracts/BindingModels/SupplierBindingModel.cs b/Contracts/BindingModels/SupplierBindingModel.cs index 20c9701..49f0469 100644 --- a/Contracts/BindingModels/SupplierBindingModel.cs +++ b/Contracts/BindingModels/SupplierBindingModel.cs @@ -11,7 +11,7 @@ namespace Contracts.BindingModels { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; - public Dictionary AvailibleProducts { get; set; } = new(); + public Dictionary AvailibleProducts { get; set; } = new(); public int Deals { get; set; } = 0; } } diff --git a/Contracts/BindingModels/SupplyBindingModel.cs b/Contracts/BindingModels/SupplyBindingModel.cs index 9611abc..44ba57c 100644 --- a/Contracts/BindingModels/SupplyBindingModel.cs +++ b/Contracts/BindingModels/SupplyBindingModel.cs @@ -8,13 +8,14 @@ using System.Threading.Tasks; namespace Contracts.BindingModels { - internal class SupplyBindingModel : ISupply + public class SupplyBindingModel : ISupply { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; + public Guid SupplierId { get; set; } public DateTime Date { get; set; } public double Price { get; set; } public SupplyStatus Status { get; set; } - public Dictionary SupplyProducts { get; set; } = new(); + public Dictionary SupplyProducts { get; set; } = new(); } } diff --git a/Contracts/BindingModels/UserBindingModel.cs b/Contracts/BindingModels/UserBindingModel.cs index d5e19ae..6c192d6 100644 --- a/Contracts/BindingModels/UserBindingModel.cs +++ b/Contracts/BindingModels/UserBindingModel.cs @@ -6,15 +6,16 @@ using System.Threading.Tasks; namespace Contracts.BindingModels { - public class UserBindingModel - { - public Guid Id { get; set; } - public string FirstName { get; set; } = string.Empty; - public string SecondName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public string PasswordHash { get; set; } = string.Empty; - public string? Password { get; set; } - public DateTime Birthday { get; set; } - public RoleBindingModel Role { get; set; } = null!; - } + public class UserBindingModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string PasswordHash { get; set; } = string.Empty; + public string? Password { get; set; } + public DateTime Birthday { get; set; } + public bool OnlyImportantMails { get; set; } + public RoleBindingModel Role { get; set; } = null!; + } } \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IProductLogic.cs b/Contracts/BusinessLogicContracts/IProductLogic.cs index b19b8e8..4711e97 100644 --- a/Contracts/BusinessLogicContracts/IProductLogic.cs +++ b/Contracts/BusinessLogicContracts/IProductLogic.cs @@ -1,4 +1,6 @@ using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; using System; using System.Collections.Generic; using System.Linq; @@ -12,6 +14,7 @@ namespace Contracts.BusinessLogicContracts bool Create(ProductBindingModel model); bool Update(ProductBindingModel model); bool Delete(ProductBindingModel model); - + List? ReadList(ProductSearchModel? model); + ProductViewModel? ReadElement(ProductSearchModel model); } } diff --git a/Contracts/BusinessLogicContracts/ISupplierLogic.cs b/Contracts/BusinessLogicContracts/ISupplierLogic.cs new file mode 100644 index 0000000..24ed51c --- /dev/null +++ b/Contracts/BusinessLogicContracts/ISupplierLogic.cs @@ -0,0 +1,20 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface ISupplierLogic + { + List? ReadList(SupplierSearchModel? model); + SupplierViewModel? ReadElement(SupplierSearchModel model); + bool Create(SupplierBindingModel model); + bool Update(SupplierBindingModel model); + bool Delete(SupplierBindingModel model); + } +} diff --git a/Contracts/BusinessLogicContracts/ISupplyDocLogic.cs b/Contracts/BusinessLogicContracts/ISupplyDocLogic.cs new file mode 100644 index 0000000..3d441c1 --- /dev/null +++ b/Contracts/BusinessLogicContracts/ISupplyDocLogic.cs @@ -0,0 +1,20 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface ISupplyDocLogic + { + List? ReadList(SupplyDocSearchModel? model); + SupplyDocViewModel? ReadElement(SupplyDocSearchModel model); + bool Create(SupplyDocBindingModel model); + bool Update(SupplyDocBindingModel model); + bool Delete(SupplyDocBindingModel model); + } +} diff --git a/Contracts/BusinessLogicContracts/ISupplyLogic.cs b/Contracts/BusinessLogicContracts/ISupplyLogic.cs new file mode 100644 index 0000000..d21ff5d --- /dev/null +++ b/Contracts/BusinessLogicContracts/ISupplyLogic.cs @@ -0,0 +1,20 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface ISupplyLogic + { + List? ReadList(SupplySearchModel? model); + SupplyViewModel? ReadElement(SupplySearchModel model); + bool Create(SupplyBindingModel model); + bool Update(SupplyBindingModel model); + bool Delete(SupplyBindingModel model); + } +} diff --git a/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs b/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs new file mode 100644 index 0000000..61cea92 --- /dev/null +++ b/Contracts/BusinessLogicContracts/ITwoFactorAuthService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface ITwoFactorAuthService + { + string GenerateCode(); + + bool Verify(string code); + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index 664b346..802f723 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -10,18 +10,20 @@ using System.Threading.Tasks; namespace Contracts.BusinessLogicContracts { - public interface IUserLogic - { - string Login(string email, string password); + public interface IUserLogic + { + string Login(string email, string password); - string Create(UserBindingModel model); + bool VerifyCode(string code); - UserViewModel Update(UserBindingModel model); + string Create(UserBindingModel model); - UserViewModel ReadElement(UserSearchModel model); + UserViewModel Update(UserBindingModel model); - IEnumerable ReadElements(UserSearchModel? model); + UserViewModel ReadElement(UserSearchModel model); - UserViewModel Delete(UserSearchModel model); - } + IEnumerable ReadElements(UserSearchModel? model); + + UserViewModel Delete(UserSearchModel model); + } } \ No newline at end of file diff --git a/Contracts/Converters/UserConverter.cs b/Contracts/Converters/UserConverter.cs index b951d11..ae6fd8a 100644 --- a/Contracts/Converters/UserConverter.cs +++ b/Contracts/Converters/UserConverter.cs @@ -8,26 +8,28 @@ using System.Threading.Tasks; namespace Contracts.Converters { - public static class UserConverter - { - public static UserViewModel ToView(UserBindingModel model) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = RoleConverter.ToView(model.Role), - }; + public static class UserConverter + { + public static UserViewModel ToView(UserBindingModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = RoleConverter.ToView(model.Role), + }; - public static UserBindingModel ToBinding(UserViewModel model) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = RoleConverter.ToBinding(model.Role), - }; - } + public static UserBindingModel ToBinding(UserViewModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = RoleConverter.ToBinding(model.Role), + }; + } } \ No newline at end of file diff --git a/Contracts/SearchModels/ProductSearchModel.cs b/Contracts/SearchModels/ProductSearchModel.cs index c038b81..1759010 100644 --- a/Contracts/SearchModels/ProductSearchModel.cs +++ b/Contracts/SearchModels/ProductSearchModel.cs @@ -10,9 +10,9 @@ namespace Contracts.SearchModels { public Guid? Id { get; set; } public string? Name { get; set; } - public double? PriceFrom { get; set; } - public double? PriceTo { get; set; } - public double? RateFrom { get; set; } - public double? RateTo { get; set; } + public double? Price { get; set; } + public double? Rate { get; set; } + public int? Amount { get; set; } + public bool? IsBeingSold { get; set; } } } diff --git a/Contracts/SearchModels/SupplierSearchModel.cs b/Contracts/SearchModels/SupplierSearchModel.cs index 2082afa..2a889e9 100644 --- a/Contracts/SearchModels/SupplierSearchModel.cs +++ b/Contracts/SearchModels/SupplierSearchModel.cs @@ -10,7 +10,7 @@ namespace Contracts.SearchModels { public Guid? Id { get; set; } public string? Name { get; set; } - public List? AvailibleProducts { get; set; } + //public List? AvailibleProducts { get; set; } public int? Deals { get; set; } } } diff --git a/Contracts/SearchModels/SupplySearchModel.cs b/Contracts/SearchModels/SupplySearchModel.cs index 9f50d53..dee63a8 100644 --- a/Contracts/SearchModels/SupplySearchModel.cs +++ b/Contracts/SearchModels/SupplySearchModel.cs @@ -1,4 +1,5 @@ -using System; +using DataModels.Enums; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -9,7 +10,8 @@ namespace Contracts.SearchModels public class SupplySearchModel { public Guid? Id { get; set; } - public List? Products { get; set; } + public string Name { get; set; } = string.Empty; + public SupplyStatus? Status { get; set; } public DateTime? DateStart { get; set; } public DateTime? DateEnd { get; set; } } diff --git a/Contracts/SearchModels/UserSearchModel.cs b/Contracts/SearchModels/UserSearchModel.cs index 2df2131..37271fc 100644 --- a/Contracts/SearchModels/UserSearchModel.cs +++ b/Contracts/SearchModels/UserSearchModel.cs @@ -6,9 +6,10 @@ using System.Threading.Tasks; namespace Contracts.SearchModels { - public class UserSearchModel - { - public Guid? Id { get; set; } - public string? Email { get; set; } - } + public class UserSearchModel + { + public Guid? Id { get; set; } + public string? Email { get; set; } + public bool? OnlyImportantMails { get; set; } + } } \ No newline at end of file diff --git a/Contracts/StorageContracts/IProductStorage.cs b/Contracts/StorageContracts/IProductStorage.cs index 49ce4e3..7d8e2bd 100644 --- a/Contracts/StorageContracts/IProductStorage.cs +++ b/Contracts/StorageContracts/IProductStorage.cs @@ -1,5 +1,6 @@ using Contracts.BindingModels; using Contracts.SearchModels; +using Contracts.ViewModels; using System; using System.Collections.Generic; using System.Linq; @@ -10,14 +11,11 @@ namespace Contracts.StorageContracts { public interface IProductStorage { - ProductBindingModel? Insert(ProductBindingModel model); - - IEnumerable GetList(ProductSearchModel? model); - - ProductBindingModel? GetElement(ProductSearchModel model); - - ProductBindingModel? Update(ProductBindingModel model); - - ProductBindingModel? Delete(ProductSearchModel model); - } + List GetFullList(); + List GetFilteredList(ProductSearchModel model); + ProductViewModel? GetElement(ProductSearchModel model); + ProductViewModel? Insert(ProductBindingModel model); + ProductViewModel? Update(ProductBindingModel model); + ProductViewModel? Delete(ProductBindingModel model); + } } diff --git a/Contracts/StorageContracts/ISupplierStorage.cs b/Contracts/StorageContracts/ISupplierStorage.cs new file mode 100644 index 0000000..dc2f1fe --- /dev/null +++ b/Contracts/StorageContracts/ISupplierStorage.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.StorageContracts +{ + public interface ISupplierStorage + { + List GetFullList(); + List GetFilteredList(SupplierSearchModel model); + SupplierViewModel? GetElement(SupplierSearchModel model); + SupplierViewModel? Insert(SupplierBindingModel model); + SupplierViewModel? Update(SupplierBindingModel model); + SupplierViewModel? Delete(SupplierBindingModel model); + } +} diff --git a/Contracts/StorageContracts/ISupplyDocStorage.cs b/Contracts/StorageContracts/ISupplyDocStorage.cs new file mode 100644 index 0000000..d10ab0e --- /dev/null +++ b/Contracts/StorageContracts/ISupplyDocStorage.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.StorageContracts +{ + public interface ISupplyDocStorage + { + List GetFullList(); + List GetFilteredList(SupplyDocSearchModel model); + SupplyDocViewModel? GetElement(SupplyDocSearchModel model); + SupplyDocViewModel? Insert(SupplyDocBindingModel model); + SupplyDocViewModel? Update(SupplyDocBindingModel model); + SupplyDocViewModel? Delete(SupplyDocBindingModel model); + } +} diff --git a/Contracts/StorageContracts/ISupplyStorage.cs b/Contracts/StorageContracts/ISupplyStorage.cs new file mode 100644 index 0000000..2f81c46 --- /dev/null +++ b/Contracts/StorageContracts/ISupplyStorage.cs @@ -0,0 +1,21 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.StorageContracts +{ + public interface ISupplyStorage + { + List GetFullList(); + List GetFilteredList(SupplySearchModel model); + SupplyViewModel? GetElement(SupplySearchModel model); + SupplyViewModel? Insert(SupplyBindingModel model); + SupplyViewModel? Update(SupplyBindingModel model); + SupplyViewModel? Delete(SupplyBindingModel model); + } +} diff --git a/Contracts/ViewModels/ProductViewModel.cs b/Contracts/ViewModels/ProductViewModel.cs index 1424a09..efb0338 100644 --- a/Contracts/ViewModels/ProductViewModel.cs +++ b/Contracts/ViewModels/ProductViewModel.cs @@ -11,7 +11,7 @@ namespace Contracts.ViewModels { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; - public int Price { get; set; } + public double Price { get; set; } public double Rating { get; set; } public bool IsBeingSold { get; set; } public int Amount { get; set; } diff --git a/Contracts/ViewModels/SupplierViewModel.cs b/Contracts/ViewModels/SupplierViewModel.cs index 337dc1d..09b2af2 100644 --- a/Contracts/ViewModels/SupplierViewModel.cs +++ b/Contracts/ViewModels/SupplierViewModel.cs @@ -1,4 +1,5 @@ -using System; +using DataModels.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -10,6 +11,6 @@ namespace Contracts.ViewModels { public Guid Id { get; set; } public string Name { get; set; } = string.Empty; - public Dictionary AvailibleProducts = new(); + public Dictionary AvailibleProducts = new(); } } diff --git a/Contracts/ViewModels/SupplyViewModel.cs b/Contracts/ViewModels/SupplyViewModel.cs index 9dca657..08f7046 100644 --- a/Contracts/ViewModels/SupplyViewModel.cs +++ b/Contracts/ViewModels/SupplyViewModel.cs @@ -1,6 +1,8 @@ using DataModels.Enums; +using DataModels.Models; using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -10,9 +12,11 @@ namespace Contracts.ViewModels public class SupplyViewModel { public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public Guid SupplierId { get; set; } public string SupplierName { get; set; } = string.Empty; public double Price { get; set; } - public Dictionary Products { get; set; } = new(); + public Dictionary Products { get; set; } = new(); public DateTime Date { get; set; } public SupplyStatus Status { get; set; } } diff --git a/Contracts/ViewModels/UserViewModel.cs b/Contracts/ViewModels/UserViewModel.cs index 6174d56..6b3ccd4 100644 --- a/Contracts/ViewModels/UserViewModel.cs +++ b/Contracts/ViewModels/UserViewModel.cs @@ -6,13 +6,14 @@ using System.Threading.Tasks; namespace Contracts.ViewModels { - public class UserViewModel - { - public Guid Id { get; set; } - public string FirstName { get; set; } = string.Empty; - public string SecondName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public DateTime Birthday { get; set; } - public RoleViewModel Role { get; set; } = null!; - } + public class UserViewModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public DateTime Birthday { get; set; } + public bool OnlyImportantMails { get; set; } + public RoleViewModel Role { get; set; } = null!; + } } \ No newline at end of file diff --git a/DataModels/Models/ISupplier.cs b/DataModels/Models/ISupplier.cs index d2ac2e2..4289d90 100644 --- a/DataModels/Models/ISupplier.cs +++ b/DataModels/Models/ISupplier.cs @@ -9,7 +9,7 @@ namespace DataModels.Models public interface ISupplier : IId { string Name { get; } - Dictionary AvailibleProducts { get; } + Dictionary AvailibleProducts { get; } int Deals { get; } } } diff --git a/DataModels/Models/ISupply.cs b/DataModels/Models/ISupply.cs index a728595..fff5ab5 100644 --- a/DataModels/Models/ISupply.cs +++ b/DataModels/Models/ISupply.cs @@ -11,8 +11,9 @@ namespace DataModels.Models { string Name { get; } double Price { get; } + Guid SupplierId { get; } DateTime Date { get; } SupplyStatus Status { get; } - Dictionary SupplyProducts { get; } + Dictionary SupplyProducts { get; } } } diff --git a/DataModels/Models/IUser.cs b/DataModels/Models/IUser.cs index 9eb2ef6..fb5020d 100644 --- a/DataModels/Models/IUser.cs +++ b/DataModels/Models/IUser.cs @@ -6,12 +6,13 @@ using System.Threading.Tasks; namespace DataModels.Models { - public interface IUser : IId - { - string FirstName { get; } - string SecondName { get; } - string PasswordHash { get; } - string Email { get; } - DateTime Birthday { get; } - } + public interface IUser : IId + { + string FirstName { get; } + string SecondName { get; } + string PasswordHash { get; } + string Email { get; } + DateTime Birthday { get; } + bool OnlyImportantMails { get; } + } } \ No newline at end of file diff --git a/DatabaseImplement/Database.cs b/DatabaseImplement/Database.cs index 69e2127..f17e76a 100644 --- a/DatabaseImplement/Database.cs +++ b/DatabaseImplement/Database.cs @@ -24,6 +24,11 @@ namespace DatabaseImplement public virtual DbSet Sells { get; set; } = null!; public virtual DbSet Purchases { get; set; } = null!; public virtual DbSet Products { get; set; } = null!; - public virtual DbSet MediaFiles { get; set; } = null!; + public virtual DbSet Supplies { get; set; } = null!; + public virtual DbSet SupplyProducts { get; set; } = null!; + public virtual DbSet Suppliers { get; set; } = null!; + public virtual DbSet SupplierProducts { get; set; } = null!; + + public virtual DbSet MediaFiles { get; set; } = null!; } } \ No newline at end of file diff --git a/DatabaseImplement/Implements/ProductStorage.cs b/DatabaseImplement/Implements/ProductStorage.cs index 3a1136c..feb9fc5 100644 --- a/DatabaseImplement/Implements/ProductStorage.cs +++ b/DatabaseImplement/Implements/ProductStorage.cs @@ -1,6 +1,9 @@ using Contracts.BindingModels; using Contracts.SearchModels; using Contracts.StorageContracts; +using Contracts.ViewModels; +using DatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; @@ -9,64 +12,150 @@ using System.Threading.Tasks; namespace DatabaseImplement.Implements { - public class ProductStorage : IProductStorage - { - public ProductBindingModel? Delete(ProductSearchModel model) - { - if (model.Id is null && model.Name is null && model.Price is null && model.Rate is null) - { - return null; - } + public class ProductStorage : IProductStorage + { + public ProductViewModel? Delete(ProductBindingModel model) + { + using var context = new Database(); + var element = context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .FirstOrDefault(rec => rec.Id == model.Id); + if (element != null) + { + context.Products.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } - var context = new Database(); - var product = context.Products.FirstOrDefault(p => - (model.Id.HasValue && p.Id == model.Id) || (!string.IsNullOrEmpty(p.Name) && p.Name.Contains(model.Name))); + public ProductViewModel? GetElement(ProductSearchModel model) + { + if (!model.Id.HasValue) + { + return null; + } + using var context = new Database(); + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id))?.GetViewModel; + } - if (product is null) - { - return null; - } - context.Remove(product); - context.SaveChanges(); + public List GetFilteredList(ProductSearchModel model) + { + if (!model.IsBeingSold.HasValue && !model.Price.HasValue && !model.Rate.HasValue && !model.Amount.HasValue && string.IsNullOrEmpty(model.Name)) + { + return new(); + } + using var context = new Database(); + if (model.Price.HasValue) + { + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .Where(x => x.Price <= model.Price) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + if (model.Rate.HasValue) + { + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .Where(x => x.Rate <= model.Rate) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + if (model.Amount.HasValue && model.IsBeingSold.HasValue) + { + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .Where(x => x.IsBeingSold == model.IsBeingSold && x.Amount >= model.Amount) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .Where(x => x.Name == model.Name) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } - return product.GetBindingModel(); - } + public List GetFullList() + { + using var context = new Database(); + return context.Products + .Include(x => x.Name) + .Include(x => x.Price) + .Include(x => x.IsBeingSold) + .Include(x => x.Rate) + .Include(x => x.Amount) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } - public ProductBindingModel? GetElement(ProductSearchModel model) - { - if (model.Id is null && string.IsNullOrWhiteSpace(model.Name)) - { - return null; - } - var context = new Database(); - return context.Products - .FirstOrDefault(prod => (model.Id.HasValue && prod.Id == model.Id) - || (!string.IsNullOrWhiteSpace(model.Name) && prod.Name.Contains(model.Name)))?.GetBindingModel(); - } + public ProductViewModel? Insert(ProductBindingModel model) + { + using var context = new Database(); + var newProduct = Product.Create(context, model); + if (newProduct == null) + { + return null; + } + context.Products.Add(newProduct); + context.SaveChanges(); + return newProduct.GetViewModel; + } - public IEnumerable GetList(ProductSearchModel? model) - { - if (model.Id is null && string.IsNullOrWhiteSpace(model.Name) || model.RateFrom > model.RateTo) - { - return null; - } - var context = new Database(); - return context.Products - .Where(prod => (model.Id.HasValue && prod.Id == model.Id) - || (!string.IsNullOrWhiteSpace(model.Name) && prod.Name.Contains(model.Name)) - || ((model.RateFrom.HasValue && model.RateTo.HasValue) && ((model.RateFrom < prod.Rate && prod.Rate < model.RateTo))) - || ((model.PriceFrom.HasValue && model.PriceTo.HasValue) && ((model.PriceFrom < prod.Price && prod.Price < model.PriceTo)))) - .Select(r => r.GetBindingModel()); ; - } - - public ProductBindingModel? Insert(ProductBindingModel model) - { - throw new NotImplementedException(); - } - - public ProductBindingModel? Update(ProductBindingModel model) - { - throw new NotImplementedException(); - } - } + public ProductViewModel? Update(ProductBindingModel model) + { + using var context = new Database(); + using var transaction = context.Database.BeginTransaction(); + try + { + var product = context.Products.FirstOrDefault(rec => + rec.Id == model.Id); + if (product == null) + { + return null; + } + product.Update(model); + context.SaveChanges(); + transaction.Commit(); + return product.GetViewModel; + } + catch + { + transaction.Rollback(); + throw; + } + } + } } diff --git a/DatabaseImplement/Implements/RoleStorage.cs b/DatabaseImplement/Implements/RoleStorage.cs index a1d8e63..390848b 100644 --- a/DatabaseImplement/Implements/RoleStorage.cs +++ b/DatabaseImplement/Implements/RoleStorage.cs @@ -32,7 +32,7 @@ namespace DatabaseImplement.Implements public RoleBindingModel? GetElement(RoleSearchModel model) { - if (model.Id is null && string.IsNullOrWhiteSpace(model.Name)) + if (model.Id is null && string.IsNullOrWhiteSpace(model?.Name)) { return null; } @@ -46,7 +46,7 @@ namespace DatabaseImplement.Implements public IEnumerable GetList(RoleSearchModel? model) { var context = new Database(); - if (model is null && string.IsNullOrWhiteSpace(model.Name)) + if (model is null && string.IsNullOrWhiteSpace(model?.Name)) { return context.Roles.Select(r => r.GetBindingModel()); } diff --git a/DatabaseImplement/Implements/SupplierStorage.cs b/DatabaseImplement/Implements/SupplierStorage.cs new file mode 100644 index 0000000..c44c0bf --- /dev/null +++ b/DatabaseImplement/Implements/SupplierStorage.cs @@ -0,0 +1,121 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using DatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Implements +{ + public class SupplierStorage : ISupplierStorage + { + public SupplierViewModel? Delete(SupplierBindingModel model) + { + using var context = new Database(); + var element = context.Suppliers + .Include(x => x.Products) + .FirstOrDefault(rec => rec.Id == model.Id); + if (element != null) + { + context.Suppliers.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } + + public SupplierViewModel? GetElement(SupplierSearchModel model) + { + if (!model.Id.HasValue) + { + return null; + } + using var context = new Database(); + return context.Suppliers + .Include(x => x.Name) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id))?.GetViewModel; + } + + public List GetFilteredList(SupplierSearchModel model) + { + if (string.IsNullOrEmpty(model.Name) && !model.Deals.HasValue) + { + return new(); + } + using var context = new Database(); + if (!string.IsNullOrEmpty(model.Name)) + { + return context.Suppliers + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => model.Name == x.Name) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + return context.Suppliers + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => model.Deals <= x.Deals) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + + public List GetFullList() + { + using var context = new Database(); + return context.Suppliers + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + + public SupplierViewModel? Insert(SupplierBindingModel model) + { + using var context = new Database(); + var newProduct = Supplier.Create(context, model); + if (newProduct == null) + { + return null; + } + context.Suppliers.Add(newProduct); + context.SaveChanges(); + return newProduct.GetViewModel; + } + + public SupplierViewModel? Update(SupplierBindingModel model) + { + using var context = new Database(); + using var transaction = context.Database.BeginTransaction(); + try + { + var product = context.Suppliers.FirstOrDefault(rec => + rec.Id == model.Id); + if (product == null) + { + return null; + } + product.Update(context, model); + context.SaveChanges(); + product.UpdateProducts(context, model); + transaction.Commit(); + return product.GetViewModel; + } + catch + { + transaction.Rollback(); + throw; + } + } + } +} diff --git a/DatabaseImplement/Implements/SupplyStorage.cs b/DatabaseImplement/Implements/SupplyStorage.cs new file mode 100644 index 0000000..64d3c8f --- /dev/null +++ b/DatabaseImplement/Implements/SupplyStorage.cs @@ -0,0 +1,147 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using DatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Implements +{ + public class SupplyStorage : ISupplyStorage + { + public SupplyViewModel? Delete(SupplyBindingModel model) + { + using var context = new Database(); + var element = context.Supplies + .Include(x => x.Products) + .FirstOrDefault(rec => rec.Id == model.Id); + if (element != null) + { + context.Supplies.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } + + public SupplyViewModel? GetElement(SupplySearchModel model) + { + if (!model.Id.HasValue) + { + return null; + } + using var context = new Database(); + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id))?.GetViewModel; + } + + public List GetFilteredList(SupplySearchModel model) + { + if (!model.DateStart.HasValue && !model.DateEnd.HasValue && model.Status == null) + { + return new(); + } + using var context = new Database(); + if (model.DateStart.HasValue && model.DateEnd.HasValue) + { + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => x.Id == model.Id || model.DateStart <= x.Date && x.Date <= model.DateEnd) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + if (model.DateEnd.HasValue) + { + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => x.Id == model.Id || x.Date <= model.DateEnd) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + if (model.DateStart.HasValue) + { + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => x.Id == model.Id || model.DateStart <= x.Date) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Where(x => model.Status.Equals(x.Status)) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + + public List GetFullList() + { + using var context = new Database(); + return context.Supplies + .Include(x => x.Supplier) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .ToList() + .Select(x => x.GetViewModel) + .ToList(); + } + + public SupplyViewModel? Insert(SupplyBindingModel model) + { + using var context = new Database(); + var newProduct = Supply.Create(context, model); + if (newProduct == null) + { + return null; + } + context.Supplies.Add(newProduct); + context.SaveChanges(); + return newProduct.GetViewModel; + } + + public SupplyViewModel? Update(SupplyBindingModel model) + { + using var context = new Database(); + using var transaction = context.Database.BeginTransaction(); + try + { + var product = context.Supplies.FirstOrDefault(rec => + rec.Id == model.Id); + if (product == null) + { + return null; + } + product.Update(model); + context.SaveChanges(); + product.UpdateProducts(context, model); + transaction.Commit(); + return product.GetViewModel; + } + catch + { + transaction.Rollback(); + throw; + } + } + } +} diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs index fdbdf0a..2755bbf 100644 --- a/DatabaseImplement/Implements/UserStorage.cs +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -12,98 +12,99 @@ using System.Threading.Tasks; namespace DatabaseImplement.Implements { - public class UserStorage : IUserStorage - { - public UserBindingModel? Delete(UserSearchModel model) - { - if (model.Id is null && model.Email is null) - { - return null; - } + public class UserStorage : IUserStorage + { + public UserBindingModel? Delete(UserSearchModel model) + { + if (model.Id is null && model.Email is null) + { + return null; + } - var context = new Database(); - var user = context.Users.FirstOrDefault(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))); + var context = new Database(); + var user = context.Users.FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))); - if (user is null) - { - return null; - } - context.Remove(user); - context.SaveChanges(); + if (user is null) + { + return null; + } + context.Remove(user); + context.SaveChanges(); - return user.GetBindingModel(); - } + return user.GetBindingModel(); + } - public UserBindingModel? GetElement(UserSearchModel model) - { - if (model.Id is null && model.Email is null) - { - return null; - } - var context = new Database(); - return context.Users - .Include(u => u.Role) - .FirstOrDefault(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) - ?.GetBindingModel(); - } + public UserBindingModel? GetElement(UserSearchModel model) + { + if (model.Id is null && model.Email is null) + { + return null; + } + var context = new Database(); + return context.Users + .Include(u => u.Role) + .FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) + ?.GetBindingModel(); + } - public IEnumerable GetList(UserSearchModel? model) - { - var context = new Database(); - if (model is null) - { - return context.Users - .Include(u => u.Role) - .Select(r => r.GetBindingModel()); - } - if (model.Id is null && model.Email is null) - { - return []; - } - return context.Users - .Where(u => - (model.Id.HasValue && u.Id == model.Id) - || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) - .Include(u => u.Role) - .Select(r => r.GetBindingModel()); - } + public IEnumerable GetList(UserSearchModel? model) + { + var context = new Database(); + if (model is null) + { + return context.Users + .Include(u => u.Role) + .Select(r => r.GetBindingModel()); + } + if (model.Id is null && model.Email is null && !model.OnlyImportantMails is null) + { + return []; + } + return context.Users + .Where(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email)) + || (model.OnlyImportantMails.HasValue && u.OnlyImportantMails == model.OnlyImportantMails)) + .Include(u => u.Role) + .Select(r => r.GetBindingModel()); + } - public UserBindingModel? Insert(UserBindingModel model) - { - var context = new Database(); - var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); - if (role is null) - { - return null; - } - var newUser = Models.User.ToUserFromBinding(model, role); + public UserBindingModel? Insert(UserBindingModel model) + { + var context = new Database(); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); + if (role is null) + { + return null; + } + var newUser = Models.User.ToUserFromBinding(model, role); - context.Users.Add(newUser); - context.SaveChanges(); + context.Users.Add(newUser); + context.SaveChanges(); - return newUser.GetBindingModel(); - } + return newUser.GetBindingModel(); + } - public UserBindingModel? Update(UserBindingModel model) - { - var context = new Database(); - var user = context.Users - .FirstOrDefault(u => u.Id == model.Id); - var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); + public UserBindingModel? Update(UserBindingModel model) + { + var context = new Database(); + var user = context.Users + .FirstOrDefault(u => u.Id == model.Id); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); - if (user is null || role is null) - { - return null; - } + if (user is null || role is null) + { + return null; + } - user.Update(model, role); + user.Update(model, role); - context.SaveChanges(); - return user.GetBindingModel(); - } - } + context.SaveChanges(); + return user.GetBindingModel(); + } + } } \ No newline at end of file diff --git a/DatabaseImplement/Migrations/20240622162300_second.Designer.cs b/DatabaseImplement/Migrations/20240622162300_second.Designer.cs new file mode 100644 index 0000000..7c32992 --- /dev/null +++ b/DatabaseImplement/Migrations/20240622162300_second.Designer.cs @@ -0,0 +1,326 @@ +// +using System; +using DatabaseImplement; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DatabaseImplement.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20240622162300_second")] + partial class second + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DatabaseImplement.Models.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("MediaFiles"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("integer"); + + b.Property("IsBeingSold") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("double precision"); + + b.Property("Rate") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Purchase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DatePurchase") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Purchases"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Sell", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateSell") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Sells"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Deals") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplierProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("SupplierId"); + + b.ToTable("SupplierProducts"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("double precision"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SupplierId"); + + b.ToTable("Supplies"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplyProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("SupplyId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("SupplyId"); + + b.ToTable("SupplyProducts"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Birthday") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("OnlyImportantMails") + .HasColumnType("boolean"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecondName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplierProduct", b => + { + b.HasOne("DatabaseImplement.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DatabaseImplement.Models.Supplier", "Supplier") + .WithMany("Products") + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.HasOne("DatabaseImplement.Models.Supplier", "Supplier") + .WithMany() + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplyProduct", b => + { + b.HasOne("DatabaseImplement.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DatabaseImplement.Models.Supply", "Supply") + .WithMany("Products") + .HasForeignKey("SupplyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Supply"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.HasOne("DatabaseImplement.Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supplier", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.Navigation("Products"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DatabaseImplement/Migrations/20240622162300_second.cs b/DatabaseImplement/Migrations/20240622162300_second.cs new file mode 100644 index 0000000..a0bb6ac --- /dev/null +++ b/DatabaseImplement/Migrations/20240622162300_second.cs @@ -0,0 +1,221 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DatabaseImplement.Migrations +{ + /// + public partial class second : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "OnlyImportantMails", + table: "Users", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "MediaFiles", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Location = table.Column(type: "text", nullable: false), + ProductId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MediaFiles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Price = table.Column(type: "double precision", nullable: false), + Rate = table.Column(type: "double precision", nullable: false), + IsBeingSold = table.Column(type: "boolean", nullable: false), + Amount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Purchases", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DatePurchase = table.Column(type: "timestamp with time zone", nullable: false), + Status = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Purchases", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Sells", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + DateSell = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Sells", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Suppliers", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Deals = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Suppliers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "SupplierProducts", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + SupplierId = table.Column(type: "uuid", nullable: false), + ProductId = table.Column(type: "uuid", nullable: false), + Count = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupplierProducts", x => x.Id); + table.ForeignKey( + name: "FK_SupplierProducts_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SupplierProducts_Suppliers_SupplierId", + column: x => x.SupplierId, + principalTable: "Suppliers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Supplies", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Price = table.Column(type: "double precision", nullable: false), + SupplierId = table.Column(type: "uuid", nullable: false), + Date = table.Column(type: "timestamp with time zone", nullable: false), + Status = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Supplies", x => x.Id); + table.ForeignKey( + name: "FK_Supplies_Suppliers_SupplierId", + column: x => x.SupplierId, + principalTable: "Suppliers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "SupplyProducts", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + SupplyId = table.Column(type: "uuid", nullable: false), + ProductId = table.Column(type: "uuid", nullable: false), + Count = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SupplyProducts", x => x.Id); + table.ForeignKey( + name: "FK_SupplyProducts_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SupplyProducts_Supplies_SupplyId", + column: x => x.SupplyId, + principalTable: "Supplies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SupplierProducts_ProductId", + table: "SupplierProducts", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_SupplierProducts_SupplierId", + table: "SupplierProducts", + column: "SupplierId"); + + migrationBuilder.CreateIndex( + name: "IX_Supplies_SupplierId", + table: "Supplies", + column: "SupplierId"); + + migrationBuilder.CreateIndex( + name: "IX_SupplyProducts_ProductId", + table: "SupplyProducts", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_SupplyProducts_SupplyId", + table: "SupplyProducts", + column: "SupplyId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MediaFiles"); + + migrationBuilder.DropTable( + name: "Purchases"); + + migrationBuilder.DropTable( + name: "Sells"); + + migrationBuilder.DropTable( + name: "SupplierProducts"); + + migrationBuilder.DropTable( + name: "SupplyProducts"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "Supplies"); + + migrationBuilder.DropTable( + name: "Suppliers"); + + migrationBuilder.DropColumn( + name: "OnlyImportantMails", + table: "Users"); + } + } +} diff --git a/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs b/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs index 875158d..80c3363 100644 --- a/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs +++ b/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs @@ -22,6 +22,72 @@ namespace DatabaseImplement.Migrations NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("DatabaseImplement.Models.MediaFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Location") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("MediaFiles"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("integer"); + + b.Property("IsBeingSold") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("double precision"); + + b.Property("Rate") + .HasColumnType("double precision"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Purchase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DatePurchase") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Purchases"); + }); + modelBuilder.Entity("DatabaseImplement.Models.Role", b => { b.Property("Id") @@ -37,6 +103,115 @@ namespace DatabaseImplement.Migrations b.ToTable("Roles"); }); + modelBuilder.Entity("DatabaseImplement.Models.Sell", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DateSell") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Sells"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supplier", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Deals") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Suppliers"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplierProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("SupplierId"); + + b.ToTable("SupplierProducts"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Price") + .HasColumnType("double precision"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("SupplierId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SupplierId"); + + b.ToTable("Supplies"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplyProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Count") + .HasColumnType("integer"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("SupplyId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.HasIndex("SupplyId"); + + b.ToTable("SupplyProducts"); + }); + modelBuilder.Entity("DatabaseImplement.Models.User", b => { b.Property("Id") @@ -54,6 +229,9 @@ namespace DatabaseImplement.Migrations .IsRequired() .HasColumnType("text"); + b.Property("OnlyImportantMails") + .HasColumnType("boolean"); + b.Property("PasswordHash") .IsRequired() .HasColumnType("text"); @@ -72,6 +250,55 @@ namespace DatabaseImplement.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("DatabaseImplement.Models.SupplierProduct", b => + { + b.HasOne("DatabaseImplement.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DatabaseImplement.Models.Supplier", "Supplier") + .WithMany("Products") + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.HasOne("DatabaseImplement.Models.Supplier", "Supplier") + .WithMany() + .HasForeignKey("SupplierId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Supplier"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.SupplyProduct", b => + { + b.HasOne("DatabaseImplement.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DatabaseImplement.Models.Supply", "Supply") + .WithMany("Products") + .HasForeignKey("SupplyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + + b.Navigation("Supply"); + }); + modelBuilder.Entity("DatabaseImplement.Models.User", b => { b.HasOne("DatabaseImplement.Models.Role", "Role") @@ -80,6 +307,16 @@ namespace DatabaseImplement.Migrations b.Navigation("Role"); }); + + modelBuilder.Entity("DatabaseImplement.Models.Supplier", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.Supply", b => + { + b.Navigation("Products"); + }); #pragma warning restore 612, 618 } } diff --git a/DatabaseImplement/Models/Product.cs b/DatabaseImplement/Models/Product.cs index bc14d3e..7edb9f4 100644 --- a/DatabaseImplement/Models/Product.cs +++ b/DatabaseImplement/Models/Product.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace DatabaseImplement.Models { @@ -55,7 +56,21 @@ namespace DatabaseImplement.Models Amount = model.Amount }; - public void Update(ProductBindingModel model, Product product) + public static Product Create(Database context, ProductBindingModel model) + { + return new Product() + { + Id = model.Id, + Name = model.Name, + Price = model.Price, + Rate = model.Rate, + IsBeingSold = model.IsBeingSold, + Amount = model.Amount + }; + } + + + public void Update(ProductBindingModel model) { if (model is null) { @@ -68,5 +83,22 @@ namespace DatabaseImplement.Models IsBeingSold = model.IsBeingSold; Amount = model.Amount; } - } + + public ProductViewModel GetViewModel + { + get + { + var context = new Database(); + return new() + { + Id = Id, + Name = Name, + Price = Price, + IsBeingSold = IsBeingSold, + Rating = Rate, + Amount = Amount + }; + } + } + } } diff --git a/DatabaseImplement/Models/Supplier.cs b/DatabaseImplement/Models/Supplier.cs new file mode 100644 index 0000000..2901ee2 --- /dev/null +++ b/DatabaseImplement/Models/Supplier.cs @@ -0,0 +1,109 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using DataModels.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace DatabaseImplement.Models +{ + public class Supplier : ISupplier + { + public Guid Id { get; set; } + [Required] + public string Name { get; set; } = string.Empty; + [Required] + public int Deals { get; set; } + private Dictionary? _availibleProducts = null; + [NotMapped] + public Dictionary AvailibleProducts + { + get + { + if (_availibleProducts == null) + { + _availibleProducts = Products + .ToDictionary(recPC => recPC.Id, recPC => + (recPC.Product as IProduct, recPC.Count)); + } + return _availibleProducts; + } + } + [ForeignKey("SupplierId")] + public virtual List Products { get; set; } = new(); + + public static Supplier Create(Database context, SupplierBindingModel model) + { + return new Supplier() + { + Id = model.Id, + Name = model.Name, + Products = model.AvailibleProducts.Select(x => new + SupplierProduct + { + Product = context.Products.First(y => y.Id == x.Key), + Count = x.Value.Item2 + }).ToList() + }; + } + public void Update(Database context, SupplierBindingModel model) + { + Name = model.Name; + Products = model.AvailibleProducts.Select(x => new + SupplierProduct + { + Product = context.Products.First(y => y.Id == x.Key), + Count = x.Value.Item2 + }).ToList(); + } + public SupplierViewModel GetViewModel + { + get + { + var context = new Database(); + return new() + { + Id = Id, + Name = Name, + AvailibleProducts = AvailibleProducts + }; + } + } + public void UpdateProducts(Database context, SupplierBindingModel model) + { + var supplierProducts = context.SupplierProducts.Where(rec => + rec.Id == model.Id).ToList(); + if (supplierProducts != null && supplierProducts.Count > 0) + { // удалили те, которых нет в модели + context.SupplierProducts.RemoveRange(supplierProducts.Where(rec + => !model.AvailibleProducts.ContainsKey(rec.ProductId))); + context.SaveChanges(); + // обновили количество у существующих записей + foreach (var updateProduct in supplierProducts) + { + updateProduct.Count = model.AvailibleProducts[updateProduct.ProductId].Item2; + model.AvailibleProducts.Remove(updateProduct.ProductId); + } + context.SaveChanges(); + } + var supplier = context.Suppliers.First(x => x.Id == Id); + foreach (var pc in model.AvailibleProducts) + { + context.SupplierProducts.Add(new SupplierProduct + { + Supplier = supplier, + Product = context.Products.First(x => x.Id == pc.Key), + Count = pc.Value.Item2 + }); + context.SaveChanges(); + } + _availibleProducts = null; + } + } +} diff --git a/DatabaseImplement/Models/SupplierProduct.cs b/DatabaseImplement/Models/SupplierProduct.cs new file mode 100644 index 0000000..95ce3af --- /dev/null +++ b/DatabaseImplement/Models/SupplierProduct.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Models +{ + public class SupplierProduct + { + public Guid Id { get; set; } + [Required] + public Guid SupplierId { get; set; } + [Required] + public Guid ProductId { get; set; } + [Required] + public int Count { get; set; } + public virtual Supplier Supplier { get; set; } = new(); + public virtual Product Product { get; set; } = new(); + } +} diff --git a/DatabaseImplement/Models/Supply.cs b/DatabaseImplement/Models/Supply.cs new file mode 100644 index 0000000..64e75f4 --- /dev/null +++ b/DatabaseImplement/Models/Supply.cs @@ -0,0 +1,118 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using DataModels.Enums; +using DataModels.Models; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Models +{ + public class Supply : ISupply + { + public Guid Id { get; set; } + [Required] + public string Name { get; set; } = string.Empty; + [Required] + public double Price { get; set; } + [Required] + public Guid SupplierId { get; set; } + [Required] + public DateTime Date { get; set; } + [Required] + public SupplyStatus Status { get; set; } = SupplyStatus.Pending; + private Dictionary? _supplyProducts = null; + [NotMapped] + public Dictionary SupplyProducts + { + get + { + if (_supplyProducts == null) + { + _supplyProducts = Products + .ToDictionary(recPC => recPC.Id, recPC => + (recPC.Product as IProduct, recPC.Count)); + } + return _supplyProducts; + } + } + [ForeignKey("SupplyId")] + public virtual List Products { get; set; } = new(); + public virtual Supplier Supplier { get; set; } + public static Supply Create(Database context, SupplyBindingModel model) + { + return new Supply() + { + Id = model.Id, + Name = model.Name, + Price = model.Price, + Date = model.Date, + SupplierId = model.SupplierId, + Products = model.SupplyProducts.Select(x => new + SupplyProduct + { + Product = context.Products.First(y => y.Id == x.Key), + Count = x.Value.Item2 + }).ToList() + }; + } + public void Update(SupplyBindingModel model) + { + Name = model.Name; + Price = model.Price; + } + public SupplyViewModel GetViewModel + { + get + { + var context = new Database(); + return new() + { + Id = Id, + Name = Name, + Price = Price, + Products = SupplyProducts, + Date = Date, + Status = Status, + SupplierName = context.Suppliers.FirstOrDefault(x => x.Id == Id)?.Name ?? string.Empty, + }; + } + } + public void UpdateProducts(Database context, SupplyBindingModel model) + { + var supplyProducts = context.SupplyProducts.Where(rec => + rec.Id == model.Id).ToList(); + if (supplyProducts != null && supplyProducts.Count > 0) + { // удалили те, которых нет в модели + context.SupplyProducts.RemoveRange(supplyProducts.Where(rec + => !model.SupplyProducts.ContainsKey(rec.ProductId))); + context.SaveChanges(); + // обновили количество у существующих записей + foreach (var updateProduct in supplyProducts) + { + updateProduct.Count = model.SupplyProducts[updateProduct.ProductId].Item2; + model.SupplyProducts.Remove(updateProduct.ProductId); + } + context.SaveChanges(); + } + var supply = context.Supplies.First(x => x.Id == Id); + foreach (var pc in model.SupplyProducts) + { + context.SupplyProducts.Add(new SupplyProduct + { + Supply = supply, + Product = context.Products.First(x => x.Id == pc.Key), + Count = pc.Value.Item2 + }); + context.SaveChanges(); + } + _supplyProducts = null; + } + } +} diff --git a/DatabaseImplement/Models/SupplyProduct.cs b/DatabaseImplement/Models/SupplyProduct.cs new file mode 100644 index 0000000..f55ed47 --- /dev/null +++ b/DatabaseImplement/Models/SupplyProduct.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Models +{ + public class SupplyProduct + { + public Guid Id { get; set; } + [Required] + public Guid SupplyId { get; set; } + [Required] + public Guid ProductId { get; set; } + [Required] + public int Count { get; set; } + public virtual Supply Supply { get; set; } = new(); + public virtual Product Product { get; set; } = new(); + } +} diff --git a/DatabaseImplement/Models/User.cs b/DatabaseImplement/Models/User.cs index a2f2ecf..4de0fd1 100644 --- a/DatabaseImplement/Models/User.cs +++ b/DatabaseImplement/Models/User.cs @@ -4,6 +4,7 @@ using Contracts.ViewModels; using DataModels.Models; using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; @@ -11,72 +12,77 @@ using System.Threading.Tasks; namespace DatabaseImplement.Models { - public class User : IUser - { - public Guid Id { get; set; } + public class User : IUser + { + public Guid Id { get; set; } - [Required] - public string FirstName { get; set; } = string.Empty; + [Required] + public string FirstName { get; set; } = string.Empty; - [Required] - public string SecondName { get; set; } = string.Empty; + [Required] + public string SecondName { get; set; } = string.Empty; - [Required] - public string PasswordHash { get; set; } = string.Empty; + [Required] + public string PasswordHash { get; set; } = string.Empty; - [Required] - public string Email { get; set; } = string.Empty; + [Required] + public string Email { get; set; } = string.Empty; - [Required] - public DateTime Birthday { get; set; } + [Required] + public DateTime Birthday { get; set; } - public Role? Role { get; set; } + public Role? Role { get; set; } - public UserBindingModel GetBindingModel() => new() - { - Id = Id, - FirstName = FirstName, - SecondName = SecondName, - Email = Email, - PasswordHash = PasswordHash, - Birthday = Birthday, - Role = Role?.GetBindingModel() ?? new() - }; + public bool OnlyImportantMails { get; set; } = false; - public static User ToUserFromView(UserViewModel model, Role role) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - Birthday = model.Birthday, - Role = role - }; + public UserBindingModel GetBindingModel() => new() + { + Id = Id, + FirstName = FirstName, + SecondName = SecondName, + Email = Email, + PasswordHash = PasswordHash, + Birthday = Birthday, + OnlyImportantMails = OnlyImportantMails, + Role = Role?.GetBindingModel() ?? new() + }; - public static User ToUserFromBinding(UserBindingModel model, Role role) => new() - { - Id = model.Id, - FirstName = model.FirstName, - SecondName = model.SecondName, - Email = model.Email, - PasswordHash = model.PasswordHash, - Birthday = model.Birthday, - Role = role - }; + public static User ToUserFromView(UserViewModel model, Role role) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + Role = role + }; - public void Update(UserBindingModel model, Role role) - { - if (model is null) - { - throw new ArgumentNullException("Update user: binding model is null"); - } + public static User ToUserFromBinding(UserBindingModel model, Role role) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + PasswordHash = model.PasswordHash, + Birthday = model.Birthday, + OnlyImportantMails = model.OnlyImportantMails, + Role = role + }; - Email = model.Email; - FirstName = model.FirstName; - SecondName = model.SecondName; - PasswordHash = model.PasswordHash; - Birthday = model.Birthday; - Role = role; - } - } + public void Update(UserBindingModel model, Role role) + { + if (model is null) + { + throw new ArgumentNullException("Update user: binding model is null"); + } + + Email = model.Email ?? Email; + FirstName = model.FirstName ?? FirstName; + SecondName = model.SecondName ?? SecondName; + PasswordHash = model.PasswordHash ?? PasswordHash; + Birthday = model.Birthday; + OnlyImportantMails = model.OnlyImportantMails; + Role = role ?? Role; + } + } } \ No newline at end of file diff --git a/RestAPI/Controllers/UserController.cs b/RestAPI/Controllers/UserController.cs index adae152..0047df3 100644 --- a/RestAPI/Controllers/UserController.cs +++ b/RestAPI/Controllers/UserController.cs @@ -46,6 +46,21 @@ namespace RestAPI.Controllers } } + [HttpPost] + public IResult VerifyCode([FromBody] VerifyCodeData data) + { + try + { + var res = _userLogic.VerifyCode(data.code); + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error verify code"); + return Results.Problem(ex.Message); + } + } + [HttpPost] public IResult Registration([FromBody] UserBindingModel model) { @@ -128,4 +143,5 @@ namespace RestAPI.Controllers } public record class UserData(string email, string password); + public record class VerifyCodeData(string code); } \ No newline at end of file diff --git a/RestAPI/Program.cs b/RestAPI/Program.cs index 1e6c8aa..f71b32a 100644 --- a/RestAPI/Program.cs +++ b/RestAPI/Program.cs @@ -20,6 +20,7 @@ builder.Logging.AddLog4Net("log4net.config"); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -34,7 +35,7 @@ builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { - c.SwaggerDoc(VERSION, new OpenApiInfo { Title = TITLE, Version = VERSION }); + c.SwaggerDoc(VERSION, new OpenApiInfo { Title = TITLE, Version = VERSION }); }); var app = builder.Build(); @@ -46,15 +47,15 @@ var mailSender = app.Services.GetService(); string? getSection(string section) => builder.Configuration?.GetSection(section)?.Value?.ToString(); jwtProvider?.SetupJwtOptions(new() { - SecretKey = getSection("JwtOptions:SecretKey") ?? string.Empty, - ExpiresHours = Convert.ToInt16(getSection("JwtOptions:ExpiresHours")) + SecretKey = getSection("JwtOptions:SecretKey") ?? string.Empty, + ExpiresHours = Convert.ToInt16(getSection("JwtOptions:ExpiresHours")) }); mailSender?.SetupMailOptions(new() { - Email = getSection("MailOptions:Email") ?? string.Empty, - Password = getSection("MailOptions:Password") ?? string.Empty, - SmtpClientHost = getSection("MailOptions:SmtpClientHost") ?? string.Empty, - SmtpClientPort = Convert.ToInt16(getSection("MailOptions:SmtpClientPort")) + Email = getSection("MailOptions:Email") ?? string.Empty, + Password = getSection("MailOptions:Password") ?? string.Empty, + SmtpClientHost = getSection("MailOptions:SmtpClientHost") ?? string.Empty, + SmtpClientPort = Convert.ToInt16(getSection("MailOptions:SmtpClientPort")) }); #endregion Setup config @@ -62,8 +63,8 @@ mailSender?.SetupMailOptions(new() // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{VERSION}/swagger.json", $"{TITLE} {VERSION}")); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{VERSION}/swagger.json", $"{TITLE} {VERSION}")); } app.UseHttpsRedirection(); diff --git a/WebApp/Pages/Login.cshtml.cs b/WebApp/Pages/Login.cshtml.cs index 4a53a79..46144ad 100644 --- a/WebApp/Pages/Login.cshtml.cs +++ b/WebApp/Pages/Login.cshtml.cs @@ -20,9 +20,9 @@ namespace WebApp.Pages throw new Exception("Something wrong LOL!"); } - this.SetJWT((string)response); + TempData["jwt"] = (string)response; - return RedirectToPage("Index"); + return RedirectToPage("TwoFactor"); } } } \ No newline at end of file diff --git a/WebApp/Pages/SignUp.cshtml b/WebApp/Pages/SignUp.cshtml index fb018dc..31c7539 100644 --- a/WebApp/Pages/SignUp.cshtml +++ b/WebApp/Pages/SignUp.cshtml @@ -74,6 +74,13 @@ +
+ + +
+ + + + + +

+ Didn't receive it? + + Resend code + +

+ + \ No newline at end of file diff --git a/WebApp/Pages/TwoFactor.cshtml.cs b/WebApp/Pages/TwoFactor.cshtml.cs new file mode 100644 index 0000000..058e2d6 --- /dev/null +++ b/WebApp/Pages/TwoFactor.cshtml.cs @@ -0,0 +1,35 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace WebApp.Pages +{ + public class TwoFactorModel : PageModel + { + [BindProperty] + public int[] Code { get; set; } = []; + + public void OnGet() + { + } + + public IActionResult OnPost(string[] code) + { + var stringCode = string.Join(string.Empty, Code); + if (string.IsNullOrEmpty(stringCode)) + { + throw new Exception("Looo"); + } + var response = (string)APIClient.PostRequest("user/verifycode", new { code = stringCode }); + var isCorrect = Convert.ToBoolean(response); + if (isCorrect) + { + this.SetJWT((string)TempData["jwt"]); + return RedirectToPage("Index"); + } + else + { + throw new Exception("Wrong code! Please retry"); + } + } + } +} \ No newline at end of file diff --git a/WebApp/Pages/User/Settings.cshtml b/WebApp/Pages/User/Settings.cshtml index f451fb4..949bd84 100644 --- a/WebApp/Pages/User/Settings.cshtml +++ b/WebApp/Pages/User/Settings.cshtml @@ -37,6 +37,12 @@ + + +
+ + +