diff --git a/Bank/BankDatabaseImplement/BankDB.cs b/Bank/BankDatabaseImplement/BankDB.cs index 7696258..adfe519 100644 --- a/Bank/BankDatabaseImplement/BankDB.cs +++ b/Bank/BankDatabaseImplement/BankDB.cs @@ -1,12 +1,32 @@ -using System; +using BankDatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BankDatabaseImplement { - internal class BankDB + public class BankDB : DbContext { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (optionsBuilder.IsConfigured == false) + { + optionsBuilder.UseNpgsql(@" + Host=localhost; + Port=5432; + Database=BankFullNew; + Username=postgres; + Password=55256636a;"); + } + base.OnConfiguring(optionsBuilder); + } + + public virtual DbSet Clients { set; get; } + public virtual DbSet Costs { set; get; } + public virtual DbSet CostByPurchases { set; get; } + public virtual DbSet Employees { set; get; } + public virtual DbSet Operations { set; get; } + public virtual DbSet OperationByPurchases { set; get; } + public virtual DbSet Payments { set; get; } + public virtual DbSet Purchases { set; get; } } } diff --git a/Bank/BankDatabaseImplement/Implements/ClientStorage.cs b/Bank/BankDatabaseImplement/Implements/ClientStorage.cs index 6317931..9d3469f 100644 --- a/Bank/BankDatabaseImplement/Implements/ClientStorage.cs +++ b/Bank/BankDatabaseImplement/Implements/ClientStorage.cs @@ -1,12 +1,40 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BankContracts.BindingModels; +using BankContracts.SearchModels; +using BankContracts.StoragesContracts; +using BankContracts.ViewModels; +using BankDatabaseImplement.Models; namespace BankDatabaseImplement.Implements { - internal class ClientStorage + public class ClientStorage : IClientStorage { + private void CheckSearchModel(ClientSearchModel model) + { + if (model == null) + throw new ArgumentNullException("Передаваемая модель для поиска равна нулю", nameof(model)); + if (!model.Id.HasValue && string.IsNullOrEmpty(model.PhoneNumber) && string.IsNullOrEmpty(model.Password)) + throw new ArgumentException("Все передаваемые поля поисковой модели оказались пусты или равны null"); + if (!model.Id.HasValue && (string.IsNullOrEmpty(model.PhoneNumber) && !string.IsNullOrEmpty(model.Password))) + throw new ArgumentException("Для нахождения соответствующего пользователя вместе с паролем нужен логин"); + } + public ClientViewModel? GetElement(ClientSearchModel model) + { + CheckSearchModel(model); + using var context = new BankDB(); + + return context.Clients.FirstOrDefault(x => x.PhoneNumber.Equals(model.PhoneNumber) && (string.IsNullOrEmpty(model.Password) || x.Password.Equals(model.Password)))?.GetViewModel; + } + public ClientViewModel? Insert(ClientBindingModel model) + { + if (model == null) + { + return null; + } + var newClient = Client.Create(model); + using var context = new BankDB(); + context.Clients.Add(newClient); + context.SaveChanges(); + return newClient.GetViewModel; + } } } diff --git a/Bank/BankDatabaseImplement/Implements/PaymentStorage.cs b/Bank/BankDatabaseImplement/Implements/PaymentStorage.cs index 5e0d08c..96538b8 100644 --- a/Bank/BankDatabaseImplement/Implements/PaymentStorage.cs +++ b/Bank/BankDatabaseImplement/Implements/PaymentStorage.cs @@ -1,12 +1,76 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BankContracts.BindingModels; +using BankContracts.SearchModels; +using BankContracts.StoragesContracts; +using BankContracts.ViewModels; +using BankDatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; namespace BankDatabaseImplement.Implements { - internal class PaymentStorage + public class PaymentStorage : IPaymentStorage { + private static IIncludableQueryable Payments(BankDB context) + => context.Payments.Include(x => x.OperationByPurchase).ThenInclude(x => x.Operation); + + public List GetFullList() + { + using var context = new BankDB(); + return Payments(context) + .Select(x => x.GetViewModel) + .ToList(); + } + + public List GetFilteredList(PaymentSearchModel model) + { + if (model == null) + { + throw new ArgumentNullException(nameof(model), "Получена пустая поисковая модель"); + } + if (model.DateFrom.HasValue && !model.DateTo.HasValue || model.DateTo.HasValue && !model.DateFrom.HasValue) + { + throw new ArgumentException("Получена поисковая модель только с началом или концом периода"); + } + if (!model.DateFrom.HasValue && !model.OperationId.HasValue) + { + throw new ArgumentNullException(nameof(model.OperationId), "Получена поисковая модель без OperationId"); + + } + if (!model.DateFrom.HasValue && !model.PurchaseId.HasValue) + { + throw new ArgumentNullException(nameof(model.PurchaseId), "Получена поисковая модель без PurchaseId"); + } + using var context = new BankDB(); + if (model.DateFrom.HasValue) + return Payments(context) + .Where(x => model.DateFrom.Value <= x.Date && x.Date <= model.DateTo.Value) + .Select(x => x.GetViewModel) + .ToList(); + + return Payments(context) + .Where(x => x.OperationByPurchase != null && + x.OperationByPurchase.OperationId == model.OperationId && + x.OperationByPurchase.PurchaseId == model.PurchaseId) + .Select(x => x.GetViewModel) + .ToList(); + } + public PaymentViewModel? GetElement(PaymentSearchModel model) + { + using var context = new BankDB(); + return Payments(context) + .FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id))?.GetViewModel; + } + public PaymentViewModel? Insert(PaymentBindingModel model) + { + if (model == null) + { + return null; + } + var newPayment = Payment.Create(model); + using var context = new BankDB(); + context.Payments.Add(newPayment); + context.SaveChanges(); + return newPayment.GetViewModel; + } } } diff --git a/Bank/BankDatabaseImplement/Implements/PurchaseStorage.cs b/Bank/BankDatabaseImplement/Implements/PurchaseStorage.cs index 9b32af6..78b1e34 100644 --- a/Bank/BankDatabaseImplement/Implements/PurchaseStorage.cs +++ b/Bank/BankDatabaseImplement/Implements/PurchaseStorage.cs @@ -1,12 +1,151 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Security.Cryptography.X509Certificates; +using BankContracts.BindingModels; +using BankContracts.SearchModels; +using BankContracts.StoragesContracts; +using BankContracts.ViewModels; +using BankDatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; namespace BankDatabaseImplement.Implements { - internal class PurchaseStorage + public class PurchaseStorage : IPurchaseStorage { + private void CheckSearchModel(PurchaseSearchModel model) + { + if (model == null) + throw new ArgumentNullException("Передаваемая модель для поиска равна нулю", nameof(model)); + if (!model.Id.HasValue && !model.ClientId.HasValue && !model.DateFrom.HasValue && !model.DateTo.HasValue && model.OperationsIds == null) + throw new ArgumentException("Все передаваемые поля поисковой модели оказались пусты или равны null"); + if (model.DateFrom.HasValue != model.DateTo.HasValue) + throw new ArgumentException($"Не указано начало {model.DateFrom} или конец {model.DateTo} периода для поиска по дате."); + } + public PurchaseViewModel? Delete(PurchaseBindingModel model) + { + using var context = new BankDB(); + var element = context.Purchases.FirstOrDefault(x => x.Id == model.Id); + if (element != null) + { + context.Purchases.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } + + + public List GetPaymentsFromPurchaseAndOperation(PurchaseSearchModel modelPurchase, OperationSearchModel modelOperation) + { + if (!modelPurchase.Id.HasValue) + { + throw new ArgumentNullException(nameof(modelPurchase), "Получена поисковая модель без Id"); + } + if (!modelOperation.Id.HasValue) + { + throw new ArgumentNullException(nameof(modelOperation), "Получена поисковая модель без Id"); + } + using var context = new BankDB(); + var carByPurchase = context.OperationByPurchases + .Include(x => x.Payments) + .FirstOrDefault(x => x.OperationId == modelOperation.Id && x.PurchaseId == modelPurchase.Id); + if (carByPurchase?.Payments == null) + { + throw new InvalidOperationException( + $"Не существует связи между данной операции(Id={modelOperation.Id}) и покупкой(Id={modelPurchase.Id})"); + } + return carByPurchase.Payments.Select(payment => payment.GetViewModel).ToList(); + } + + public PurchaseViewModel? GetElement(PurchaseSearchModel model) + { + using var context = new BankDB(); + if (!model.Id.HasValue) + { + return null; + } + return context.Purchases + .Include(x => x.Client) + .Include(x => x.Operations) + .FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)?.GetViewModel; + } + + public List GetFilteredList(PurchaseSearchModel model) + { + CheckSearchModel(model); + if (model.Id.HasValue) + { + var res = GetElement(model); + return res != null ? new() { res } : new(); + } + + using var context = new BankDB(); + + var query = context.Purchases.Include(x => x.Client); + IQueryable? resultQuery = null; + if (model.ClientId.HasValue) + { + + return query + .Where(x => model.ClientId == x.ClientId) + .Select(x => x.GetViewModel) + .ToList(); + } + + if (model.DateTo.HasValue) + resultQuery = query + .Include(x => x.Operations) + .ThenInclude(x => x.Operation) + .Include(x => x.Costs)! + .ThenInclude(x => x.Cost) + .Where(x => model.DateFrom <= x.DatePurchase && x.DatePurchase <= model.DateTo); + + else if (model.OperationsIds != null) + resultQuery = query + .Where(x => x.Operations.Any(x => model.OperationsIds.Contains(x.OperationId))) + .Include(x => x.Operations) + .ThenInclude(x => x.Operation); + + return resultQuery? + .Select(x => x.GetViewModel) + .ToList() ?? new(); + } + + public List GetFullList() + { + using var context = new BankDB(); + return context.Purchases + .Include(x => x.Client) + .Include(x => x.Operations) + .Select(x => x.GetViewModel) + .ToList(); + } + + public PurchaseViewModel? Insert(PurchaseBindingModel model) + { + var newPurchase = Purchase.Create(model); + if (newPurchase == null) + { + return null; + } + using var context = new BankDB(); + context.Purchases.Add(newPurchase); + context.SaveChanges(); + newPurchase.UpdateOperations(context, model); + context.SaveChanges(); + return newPurchase.GetViewModel; + } + + public PurchaseViewModel? Update(PurchaseBindingModel model) + { + using var context = new BankDB(); + var purchase = context.Purchases.FirstOrDefault(x => x.Id == model.Id); + if (purchase == null) + { + return null; + } + purchase.Update(model); + purchase.UpdateOperations(context, model); + context.SaveChanges(); + return purchase.GetViewModel; + } } } diff --git a/Bank/BankDatabaseImplement/Models/Client.cs b/Bank/BankDatabaseImplement/Models/Client.cs index 2040411..cdff700 100644 --- a/Bank/BankDatabaseImplement/Models/Client.cs +++ b/Bank/BankDatabaseImplement/Models/Client.cs @@ -1,12 +1,57 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BankContracts.BindingModels; +using BankContracts.ViewModels; +using BankDataModels; +using System.ComponentModel.DataAnnotations; namespace BankDatabaseImplement.Models { - internal class Client + public class Client : IClientModel { + public int Id { get; set; } + [Required] + public string FirstName { get; private set; } = string.Empty; + [Required] + public string LastName { get; private set; } = string.Empty; + + public string? MiddleName { get; private set; } + [Required] + public string Address { get; private set; } = string.Empty; + [Required] + public string PhoneNumber { get; private set; } = string.Empty; + [Required] + public string Email { get; set; } = string.Empty; + [Required] + public string Password { get; private set; } = string.Empty; + [Required] + public List? Purchases { get; private set; } + + public static Client Create(ClientBindingModel model) + { + if (model == null) + { + return null; + } + return new Client() + { + FirstName = model.FirstName, + LastName = model.LastName, + MiddleName = model.MiddleName, + PhoneNumber = model.PhoneNumber, + Password = model.Password, + Id = model.Id, + Email = model.Email, + }; + } + + public ClientViewModel GetViewModel => new() + { + FirstName = FirstName, + LastName = LastName, + MiddleName = MiddleName, + PhoneNumber = PhoneNumber, + Password = Password, + Id = Id, + Email = Email, + }; } } diff --git a/Bank/BankDatabaseImplement/Models/CostByPurchase.cs b/Bank/BankDatabaseImplement/Models/CostByPurchase.cs index 46b0bb4..6b34817 100644 --- a/Bank/BankDatabaseImplement/Models/CostByPurchase.cs +++ b/Bank/BankDatabaseImplement/Models/CostByPurchase.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using BankDataModels.ProxyModels; namespace BankDatabaseImplement.Models { - internal class CostByPurchase + public class CostByPurchase : CostByPurchaseModel { + [Required] + public Cost? Cost { get; private set; } + [Required] + public Purchase? Purchase { get; private set; } } } diff --git a/Bank/BankDatabaseImplement/Models/OperationByPurchase.cs b/Bank/BankDatabaseImplement/Models/OperationByPurchase.cs index 7a11b21..a5d6ac9 100644 --- a/Bank/BankDatabaseImplement/Models/OperationByPurchase.cs +++ b/Bank/BankDatabaseImplement/Models/OperationByPurchase.cs @@ -1,12 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.ComponentModel.DataAnnotations; +using BankDataModels.ProxyModels; namespace BankDatabaseImplement.Models { - internal class OperationByPurchase + public class OperationByPurchase : OperationByPurchaseModel { + [Required] + public Operation? Operation { get; private set; } + [Required] + public Purchase? Purchase { get; private set; } + [Required] + public List? Payments { get; private set; } } -} +} \ No newline at end of file diff --git a/Bank/BankDatabaseImplement/Models/Payment.cs b/Bank/BankDatabaseImplement/Models/Payment.cs index 7fa2d8b..65efd96 100644 --- a/Bank/BankDatabaseImplement/Models/Payment.cs +++ b/Bank/BankDatabaseImplement/Models/Payment.cs @@ -1,12 +1,51 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BankContracts.BindingModels; +using BankContracts.ViewModels; +using BankDataModels; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; namespace BankDatabaseImplement.Models { - internal class Payment + public class Payment : IPaymentModel { + [Required] + public int OperationByPurchaseId { get; private set; } + + [Required] + public DateOnly Date { get; private set; } + + [Required] + public double PaidPrice { get; private set; } + + public int Id { get; private set; } + + [Required] + public OperationByPurchase? OperationByPurchase { get; private set; } + + public static Payment Create(PaymentBindingModel model) + { + if (model == null) + { + return null; + } + return new Payment() + { + + Date = model.Date, + PaidPrice = model.PaidPrice, + Id = model.Id, + OperationByPurchaseId = model.OperationByPurchaseId, + }; + } + public PaymentViewModel GetViewModel => new() + { + Operation = OperationByPurchase?.Operation?.GetViewModel, + OperationByPurchase = OperationByPurchase, + FullPrice = OperationByPurchase?.Operation?.Price ?? -1, + Date = Date, + PaidPrice = PaidPrice, + Id = Id, + OperationByPurchaseId = OperationByPurchaseId, + }; } } diff --git a/Bank/BankDatabaseImplement/Models/Purchase.cs b/Bank/BankDatabaseImplement/Models/Purchase.cs index 318de8a..3e5c82c 100644 --- a/Bank/BankDatabaseImplement/Models/Purchase.cs +++ b/Bank/BankDatabaseImplement/Models/Purchase.cs @@ -1,12 +1,95 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using BankContracts.BindingModels; +using BankContracts.ViewModels; +using BankDataModels; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq.Expressions; +using BankDataModels.ProxyModels; namespace BankDatabaseImplement.Models { - internal class Purchase + public class Purchase : IPurchaseModel { + public int ClientId { get; private set; } + public DateOnly DatePurchase { get; private set; } + private Dictionary? _cachedOperations; + [NotMapped] + public Dictionary OperationsModel => _cachedOperations ??= Operations.Select(x => (OperationByPurchaseModel)x).ToDictionary(x => x.OperationId, x => x); + [NotMapped] + public List? CostsModel => null; + public int Id { get; private set; } + [Required] + public Client? Client { get; private set; } + [Required] + public List Operations { get; private set; } = new(); + [Required] + public List Costs { get; private set; } = new(); + + public static Purchase Create(PurchaseBindingModel model) + { + return new Purchase() + { + ClientId = model.ClientId, + Id = model.Id, + DatePurchase = model.DatePurchase, + + }; + } + public void Update(PurchaseBindingModel model) + { + DatePurchase = model.DatePurchase; + } + + public PurchaseViewModel GetViewModel => new() + { + OperationsModel = OperationsModel, + Id = Id, + DatePurchase = DatePurchase, + ClientId = ClientId, + ClientPhoneNumber = Client?.PhoneNumber ?? string.Empty, + + CostViewModels = Costs? + .Select(x => x.Cost.GetViewModel) + .ToList() ?? new(), + OperationViewModels = Operations? + .Select(x => x.Operation?.GetViewModel) + .ToList() ?? new() + }; + + public PurchaseViewModel GetViewModel2 => new() + { + OperationsModel = OperationsModel, + Id = Id, + DatePurchase = DatePurchase, + ClientId = ClientId, + ClientPhoneNumber = Client?.PhoneNumber ?? string.Empty, + + CostViewModels = Costs? + .Select(x => x.Cost.GetViewModel) + .ToList() ?? new() + }; + + public void UpdateOperations(BankDB context, PurchaseBindingModel model) + { + var oldOperations = context.OperationByPurchases.Where(x => x.PurchaseId == model.Id).ToDictionary(x => x.OperationId, x => x); + var newOperations = model.OperationsModel.ToDictionary( + x => x.Key, + x => new OperationByPurchase() + { + OperationId = x.Key, + PurchaseId = Id, + CountOperations = x.Value.CountOperations + } + ); + + context.RemoveRange(oldOperations.Where(x => !newOperations.ContainsKey(x.Key)).Select(x => x.Value)); + context.AddRange(newOperations.Where(x => !oldOperations.ContainsKey(x.Key)).Select(x => x.Value)); + oldOperations + .Where(x => newOperations.ContainsKey(x.Key)) + .Select(x => x.Value).ToList() + .ForEach(x => x.CountOperations = newOperations[x.OperationId].CountOperations); + context.SaveChanges(); + _cachedOperations = null; + } } }