From 8708d8b6d019a356fd56b5bfafb95c1c6ccc78b2 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 14 Jun 2023 22:47:30 +0400 Subject: [PATCH] partially done reports --- .../BusinessLogics/ReportLogic.cs | 100 ++++++++++++--- .../OfficePackage/AbstractSaveToExcel.cs | 121 +++++++++++++++--- .../OfficePackage/AbstractSaveToWord.cs | 55 +++++++- .../OfficePackage/HelperModels/ExcelInfo.cs | 3 +- .../OfficePackage/HelperModels/WordInfo.cs | 3 +- .../BindingModels/ReportBindingModel.cs | 3 +- .../BusinessLogicsContracts/IReportLogic.cs | 4 +- .../SearchModels/CookSearchModel.cs | 1 + .../SearchModels/OrderSearchModel.cs | 1 + .../StoragesContracts/IOrderStorage.cs | 2 +- .../ViewModels/ReportCookView.cs | 15 +++ .../ViewModels/ReportOrderView.cs | 15 +++ .../CanteenDatabase.cs | 2 +- .../Implements/CookStorage.cs | 19 ++- .../Implements/LunchStorage.cs | 8 +- .../Implements/OrderStorage.cs | 14 +- .../Implements/ProductStorage.cs | 4 +- .../CanteenDatabaseImplement/Models/Cook.cs | 2 +- .../Controllers/HomeController.cs | 35 +++++ .../Views/Home/Report.cshtml | 31 +++++ .../Views/Shared/_Layout.cshtml | 3 + .../Controllers/MainController.cs | 60 +++++++-- Canteen/CanteenRestApi/Cook.docx | Bin 0 -> 1454 bytes Canteen/CanteenRestApi/Cook.docx.xlsx.xls | Bin 0 -> 3056 bytes Canteen/CanteenRestApi/Cook.xlsx | Bin 0 -> 3139 bytes Canteen/CanteenRestApi/Order.xlsx | Bin 0 -> 3329 bytes .../{pdfReport => pdfReport.pdf} | Bin .../Controllers/HomeController.cs | 14 +- 28 files changed, 435 insertions(+), 80 deletions(-) create mode 100644 Canteen/CanteenContracts/ViewModels/ReportCookView.cs create mode 100644 Canteen/CanteenContracts/ViewModels/ReportOrderView.cs create mode 100644 Canteen/CanteenManagerApp/Views/Home/Report.cshtml create mode 100644 Canteen/CanteenRestApi/Cook.docx create mode 100644 Canteen/CanteenRestApi/Cook.docx.xlsx.xls create mode 100644 Canteen/CanteenRestApi/Cook.xlsx create mode 100644 Canteen/CanteenRestApi/Order.xlsx rename Canteen/CanteenRestApi/{pdfReport => pdfReport.pdf} (100%) diff --git a/Canteen/CanteenBusinessLogic/BusinessLogics/ReportLogic.cs b/Canteen/CanteenBusinessLogic/BusinessLogics/ReportLogic.cs index 8883a04..9120ded 100644 --- a/Canteen/CanteenBusinessLogic/BusinessLogics/ReportLogic.cs +++ b/Canteen/CanteenBusinessLogic/BusinessLogics/ReportLogic.cs @@ -6,6 +6,8 @@ using CanteenContracts.SearchModel; using CanteenContracts.StoragesContracts; using CanteenContracts.View; using CanteenContracts.ViewModels; +using DocumentFormat.OpenXml.Bibliography; +using System.Linq; namespace CanteenBusinessLogic.BusinessLogics { @@ -35,12 +37,11 @@ namespace CanteenBusinessLogic.BusinessLogics { var list = new List(); - // Получаем список обедов (сущность 1) за указанный период и для указанного посетителя var lunches = lunchStorage.GetFilteredList(new LunchSearchModel { DateFrom = (DateTime)model.DateAfter, DateTo = model.DateBefore, - VisitorId = model.VisitorId + VisitorId = model.UserId }); foreach (var lunch in lunches) @@ -53,16 +54,13 @@ namespace CanteenBusinessLogic.BusinessLogics Cooks = new List() }; - // Получаем связанные заказы (сущность 2) для текущего обеда var orders = lunch.LunchOrders.Keys.ToList(); foreach (var orderId in orders) { - // Получаем заказы (сущность 2) и добавляем их в список Orders var order = orderStorage.GetElement(new OrderSearchModel { Id = orderId }); record.Orders.Add(order); } - // Получаем связанных поваров (сущность 4) для текущих продуктов обеда var lunchProducts = lunch.LunchProducts.Keys.ToList(); foreach (var productId in lunchProducts) { @@ -71,7 +69,6 @@ namespace CanteenBusinessLogic.BusinessLogics foreach (var cookId in productCooks) { - // Получаем поваров (сущность 4) и добавляем их в список Cooks var cook = cookStorage.GetElement(new CookSearchModel { Id = cookId }); record.Cooks.Add(cook); } @@ -95,24 +92,77 @@ namespace CanteenBusinessLogic.BusinessLogics Lunches = GetLunchesPCView(model) }); } - public List GetCooksByLanches(ReportBindingModel model) + public List GetCooksByLanches(ReportBindingModel model) { - var list = new List(); - var listCookIds = new List(); - foreach (var lunch in model.lunches) + var list = new List(); + + var lunches = lunchStorage.GetFilteredList(new LunchSearchModel { - var lunchProducts = lunch.LunchProducts.Keys.ToList().Select(rec => productStorage.GetElement(new ProductSearchModel { Id = rec })); - foreach (var elem in lunchProducts) + DateFrom = (DateTime)model.DateAfter, + DateTo = model.DateBefore, + VisitorId = model.UserId + }); + + foreach (var lunch in lunches) + { + var record = new ReportCookView { - listCookIds.AddRange(elem.ProductCooks.Keys.ToList()); + Lunch = lunch, + Cooks = new List() + }; + var lunchProducts = lunch.LunchProducts.Keys.ToList(); + foreach (var productId in lunchProducts) + { + var product = productStorage.GetElement(new ProductSearchModel { Id = productId }); + var productCooks = product.ProductCooks.Keys.ToList(); + + foreach (var cookId in productCooks) + { + if (record.Cooks.Where(cook => cook.Id == cookId).ToList().Count == 0) + { + var cook = cookStorage.GetElement(new CookSearchModel { Id = cookId }); + record.Cooks.Add(cook); + } + } } + + list.Add(record); + } + return list; + } + + private List GetOrdersByProducts(ReportBindingModel model) + { + var list = new List(); + + var products = productStorage.GetFilteredList(new ProductSearchModel + { + ManagerId = model.UserId + }); + + foreach (var product in products) + { + var record = new ReportOrderView + { + Product = product, + Orders = new List() + }; + var productCook = product.ProductCooks.Keys.ToList(); + foreach (var cookId in productCook) + { + var orders = orderStorage.GetOrderCooksList(new OrderSearchModel { CookId = cookId }); + orders.ForEach(x => + { + if (record.Orders.Find(y => y.Id == x.Id) == null) record.Orders.Add(x); + }); + } + list.Add(record); } - list = listCookIds.Distinct().ToList().Select(rec => cookStorage.GetElement(new CookSearchModel { Id = rec })).ToList(); return list; } public void saveCooksToExcel(ReportBindingModel model) { - saveToExcel.CreateReport(new ExcelInfo() + saveToExcel.CreateCooksReport(new ExcelInfo() { FileName = model.FileName, Title = "Список поваров:", @@ -121,12 +171,30 @@ namespace CanteenBusinessLogic.BusinessLogics } public void saveCooksToWord(ReportBindingModel model) { - saveToWord.CreateDoc(new WordInfo() + saveToWord.CreateCooksDoc(new WordInfo() { FileName = model.FileName, Title = "Список поваров", Cooks = GetCooksByLanches(model) }); } + public void saveOrdersToExcel(ReportBindingModel model) + { + saveToExcel.CreateOrdersReport(new ExcelInfo() + { + FileName = model.FileName, + Title = "Список заказов:", + Orders = GetOrdersByProducts(model) + }); + } + public void saveOrdersToWord(ReportBindingModel model) + { + saveToWord.CreateOrdersDoc(new WordInfo() + { + FileName = model.FileName, + Title = "Список заказов", + Orders = GetOrdersByProducts(model) + }); + } } } diff --git a/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToExcel.cs b/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToExcel.cs index 4812d0c..d6d6ad4 100644 --- a/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToExcel.cs +++ b/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToExcel.cs @@ -5,12 +5,13 @@ using System.Text; using System.Threading.Tasks; using CanteenBusinessLogic.OfficePackage.HelperEnums; using CanteenBusinessLogic.OfficePackage.HelperModels; +using DocumentFormat.OpenXml.EMMA; namespace CanteenBusinessLogic.OfficePackage { public abstract class AbstractSaveToExcel { - public void CreateReport(ExcelInfo info) + public void CreateCooksReport(ExcelInfo info) { CreateExcel(info); InsertCellInWorksheet(new ExcelCellParameters @@ -26,35 +27,119 @@ namespace CanteenBusinessLogic.OfficePackage CellToName = "C1" }); uint rowIndex = 2; - foreach (var pc in info.Cooks) + + foreach (var reportCookView in info.Cooks) { InsertCellInWorksheet(new ExcelCellParameters { ColumnName = "A", RowIndex = rowIndex, - Text = pc.FIO, - StyleInfo = ExcelStyleInfoType.TextWithBroder - }); - InsertCellInWorksheet(new ExcelCellParameters - { - ColumnName = "B", - RowIndex = rowIndex, - Text = "", - StyleInfo = ExcelStyleInfoType.TextWithBroder - }); - InsertCellInWorksheet(new ExcelCellParameters - { - ColumnName = "C", - RowIndex = rowIndex, - Text = "", + Text = $"Обед: {reportCookView.Lunch.LunchName}, {reportCookView.Lunch.DateCreate}", StyleInfo = ExcelStyleInfoType.TextWithBroder }); MergeCells(new ExcelMergeParameters { CellFromName = "A" + rowIndex, - CellToName = "C" + rowIndex + CellToName = "E" + rowIndex }); rowIndex++; + + foreach (var cook in reportCookView.Cooks) + { + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "A", + RowIndex = rowIndex, + Text = cook.FIO, + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "B", + RowIndex = rowIndex, + Text = "", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "C", + RowIndex = rowIndex, + Text = "", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + MergeCells(new ExcelMergeParameters + { + CellFromName = "A" + rowIndex, + CellToName = "C" + rowIndex + }); + rowIndex++; + } + } + SaveExcel(info); + } + + public void CreateOrdersReport(ExcelInfo info) + { + CreateExcel(info); + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "A", + RowIndex = 1, + Text = info.Title, + StyleInfo = ExcelStyleInfoType.Title + }); + MergeCells(new ExcelMergeParameters + { + CellFromName = "A1", + CellToName = "C1" + }); + uint rowIndex = 2; + foreach (var orderView in info.Orders) + { + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "A", + RowIndex = rowIndex, + Text = $"Продукт: {orderView.Product.ProductName}, {orderView.Product.Price}", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + MergeCells(new ExcelMergeParameters + { + CellFromName = "A" + rowIndex, + CellToName = "E" + rowIndex + }); + rowIndex++; + + foreach (var order in orderView.Orders) + { + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "A", + RowIndex = rowIndex, + Text = $"Id: {order.Id}", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "B", + RowIndex = rowIndex, + Text = "", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + InsertCellInWorksheet(new ExcelCellParameters + { + ColumnName = "C", + RowIndex = rowIndex, + Text = "", + StyleInfo = ExcelStyleInfoType.TextWithBroder + }); + MergeCells(new ExcelMergeParameters + { + CellFromName = "A" + rowIndex, + CellToName = "C" + rowIndex + }); + rowIndex++; + } } SaveExcel(info); } diff --git a/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToWord.cs b/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToWord.cs index 00381c8..dcdd8fc 100644 --- a/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToWord.cs +++ b/Canteen/CanteenBusinessLogic/OfficePackage/AbstractSaveToWord.cs @@ -10,36 +10,77 @@ namespace CanteenBusinessLogic.OfficePackage { public abstract class AbstractSaveToWord { - public void CreateDoc(WordInfo info) + public void CreateCooksDoc(WordInfo info) { CreateWord(info); CreateParagraph(new WordParagraph { - Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24"}) }, + Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24" }) }, TextProperties = new WordTextProperties { Size = "24", JustificationType = WordJustificationType.Center } }); - foreach (var component in info.Cooks) + + foreach (var reportCookView in info.Cooks) { CreateParagraph(new WordParagraph { - Texts = new List<(string, WordTextProperties)> {("Повар: ", new WordTextProperties {Bold = true, Size = "24"}), - (component.FIO, new WordTextProperties {Bold = false, Size = "24"})}, + Texts = new List<(string, WordTextProperties)> + { + ("Обед: ", new WordTextProperties { Bold = true, Size = "24" }), + (reportCookView.Lunch.LunchName, new WordTextProperties { Bold = false, Size = "24" }), + (" Дата создания: ", new WordTextProperties { Bold = true, Size = "24" }), + (reportCookView.Lunch.DateCreate.ToString(), new WordTextProperties { Bold = false, Size = "24" }) + }, TextProperties = new WordTextProperties { Size = "24", JustificationType = WordJustificationType.Both } }); - } - SaveWord(info); + CreateParagraph(new WordParagraph + { + Texts = new List<(string, WordTextProperties)> + { + ("Повара: ", new WordTextProperties { Bold = true, Size = "24" }) + }, + TextProperties = new WordTextProperties + { + Size = "24", + JustificationType = WordJustificationType.Both + } + }); + + foreach (var cook in reportCookView.Cooks) + { + CreateParagraph(new WordParagraph + { + Texts = new List<(string, WordTextProperties)> + { + (cook.FIO, new WordTextProperties { Bold = false, Size = "24" }) + }, + TextProperties = new WordTextProperties + { + Size = "24", + JustificationType = WordJustificationType.Both + } + }); + } + } + + SaveWord(info); } + protected abstract void CreateWord(WordInfo info); protected abstract void CreateParagraph(WordParagraph paragraph); protected abstract void SaveWord(WordInfo info); + + internal void CreateOrdersDoc(WordInfo wordInfo) + { + throw new NotImplementedException(); + } } } diff --git a/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/ExcelInfo.cs b/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/ExcelInfo.cs index 48e5832..0d43e45 100644 --- a/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/ExcelInfo.cs +++ b/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/ExcelInfo.cs @@ -12,6 +12,7 @@ namespace CanteenBusinessLogic.OfficePackage.HelperModels { public string FileName { get; set; } public string Title { get; set; } - public List Cooks { get; set; } + public List Cooks { get; set; } + public List Orders { get; set; } } } diff --git a/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/WordInfo.cs b/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/WordInfo.cs index e664e37..67a6189 100644 --- a/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/WordInfo.cs +++ b/Canteen/CanteenBusinessLogic/OfficePackage/HelperModels/WordInfo.cs @@ -12,6 +12,7 @@ namespace CanteenBusinessLogic.OfficePackage.HelperModels { public string FileName { get; set; } public string Title { get; set; } - public List Cooks { get; set; } + public List Cooks { get; set; } + public List Orders { get; set; } } } diff --git a/Canteen/CanteenContracts/BindingModels/ReportBindingModel.cs b/Canteen/CanteenContracts/BindingModels/ReportBindingModel.cs index 658c0c6..6470568 100644 --- a/Canteen/CanteenContracts/BindingModels/ReportBindingModel.cs +++ b/Canteen/CanteenContracts/BindingModels/ReportBindingModel.cs @@ -13,6 +13,7 @@ namespace CanteenContracts.BindingModels public DateTime? DateAfter { get; set; } public DateTime? DateBefore { get; set; } public List? lunches { get; set; } - public int VisitorId { get; set; } + public List? orders { get; set; } + public int UserId { get; set; } } } diff --git a/Canteen/CanteenContracts/BusinessLogicsContracts/IReportLogic.cs b/Canteen/CanteenContracts/BusinessLogicsContracts/IReportLogic.cs index 08d3663..a596289 100644 --- a/Canteen/CanteenContracts/BusinessLogicsContracts/IReportLogic.cs +++ b/Canteen/CanteenContracts/BusinessLogicsContracts/IReportLogic.cs @@ -11,10 +11,12 @@ namespace CanteenContracts.BusinessLogicsContracts { public interface IReportLogic { - List GetCooksByLanches(ReportBindingModel model); + public List GetCooksByLanches(ReportBindingModel model); List GetLunchesPCView(ReportBindingModel model); void saveLunchesToPdfFile(ReportBindingModel model); void saveCooksToWord(ReportBindingModel model); void saveCooksToExcel(ReportBindingModel model); + public void saveOrdersToExcel(ReportBindingModel model); + public void saveOrdersToWord(ReportBindingModel model); } } diff --git a/Canteen/CanteenContracts/SearchModels/CookSearchModel.cs b/Canteen/CanteenContracts/SearchModels/CookSearchModel.cs index 0503b4d..8c00c4f 100644 --- a/Canteen/CanteenContracts/SearchModels/CookSearchModel.cs +++ b/Canteen/CanteenContracts/SearchModels/CookSearchModel.cs @@ -11,5 +11,6 @@ namespace CanteenContracts.SearchModel public int? Id { get; set; } public string? FIO { get; set; } public int? ManagerId { get; set; } + public int? OrderId { get; set; } } } diff --git a/Canteen/CanteenContracts/SearchModels/OrderSearchModel.cs b/Canteen/CanteenContracts/SearchModels/OrderSearchModel.cs index b747160..434d762 100644 --- a/Canteen/CanteenContracts/SearchModels/OrderSearchModel.cs +++ b/Canteen/CanteenContracts/SearchModels/OrderSearchModel.cs @@ -10,6 +10,7 @@ namespace CanteenContracts.SearchModel { public int? Id { get; set; } public int? VisitorId { get; set; } + public int? CookId { get; set; } } } diff --git a/Canteen/CanteenContracts/StoragesContracts/IOrderStorage.cs b/Canteen/CanteenContracts/StoragesContracts/IOrderStorage.cs index 7bf4105..84519f6 100644 --- a/Canteen/CanteenContracts/StoragesContracts/IOrderStorage.cs +++ b/Canteen/CanteenContracts/StoragesContracts/IOrderStorage.cs @@ -16,7 +16,7 @@ namespace CanteenContracts.StoragesContracts List GetFullList(); List GetFilteredList(OrderSearchModel model); OrderViewModel? GetElement(OrderSearchModel model); - OrderCookViewModel? GetOrderCookElement(OrderCookSearchModel model); + List? GetOrderCooksList(OrderSearchModel model); OrderViewModel? Insert(OrderBindingModel model); OrderViewModel? Update(OrderBindingModel model); OrderViewModel? Delete(OrderBindingModel model); diff --git a/Canteen/CanteenContracts/ViewModels/ReportCookView.cs b/Canteen/CanteenContracts/ViewModels/ReportCookView.cs new file mode 100644 index 0000000..ff8e262 --- /dev/null +++ b/Canteen/CanteenContracts/ViewModels/ReportCookView.cs @@ -0,0 +1,15 @@ +using CanteenContracts.View; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CanteenContracts.ViewModels +{ + public class ReportCookView + { + public LunchViewModel Lunch { get; set; } + public List Cooks { get; set; } + } +} diff --git a/Canteen/CanteenContracts/ViewModels/ReportOrderView.cs b/Canteen/CanteenContracts/ViewModels/ReportOrderView.cs new file mode 100644 index 0000000..26e1630 --- /dev/null +++ b/Canteen/CanteenContracts/ViewModels/ReportOrderView.cs @@ -0,0 +1,15 @@ +using CanteenContracts.View; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CanteenContracts.ViewModels +{ + public class ReportOrderView + { + public List Orders { get; set; } + public ProductViewModel Product { get; set; } + } +} diff --git a/Canteen/CanteenDatabaseImplement/CanteenDatabase.cs b/Canteen/CanteenDatabaseImplement/CanteenDatabase.cs index a924378..fc050a6 100644 --- a/Canteen/CanteenDatabaseImplement/CanteenDatabase.cs +++ b/Canteen/CanteenDatabaseImplement/CanteenDatabase.cs @@ -11,7 +11,7 @@ namespace CanteenDatabaseImplement { if (optionsBuilder.IsConfigured == false) { - optionsBuilder.UseSqlServer(@"Data Source=DESKTOP-A68O3K0;Initial Catalog=CanteenDataBase;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True"); + optionsBuilder.UseSqlServer(@"Data Source=DESKTOP-23CS6SP\SQLEXPRESS;Initial Catalog=CanteenDataBase;Integrated Security=True;MultipleActiveResultSets=True;;TrustServerCertificate=True"); } base.OnConfiguring(optionsBuilder); } diff --git a/Canteen/CanteenDatabaseImplement/Implements/CookStorage.cs b/Canteen/CanteenDatabaseImplement/Implements/CookStorage.cs index 8dca61d..089c094 100644 --- a/Canteen/CanteenDatabaseImplement/Implements/CookStorage.cs +++ b/Canteen/CanteenDatabaseImplement/Implements/CookStorage.cs @@ -4,6 +4,7 @@ using CanteenContracts.StoragesContracts; using CanteenContracts.View; using CanteenDatabaseImplement.Models; using CanteenDataModels.Models; +using FluentNHibernate.Conventions; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -25,16 +26,30 @@ namespace CanteenDatabaseImplement.Implements using var context = new CanteenDatabase(); - return context.Cooks.FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)?.GetViewModel; + return context.Cooks + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id)?.GetViewModel; } public List GetFilteredList(CookSearchModel model) { + if (!model.Id.HasValue && !model.OrderId.HasValue) + { + return new(); + } using var context = new CanteenDatabase(); return context.Cooks .Include(x => x.Manager) - .Where(x => (model.Id.HasValue && x.Id == model.Id) || (model.ManagerId.HasValue && model.ManagerId == x.ManagerId)) + .Include(x => x.Products) + .ThenInclude(x => x.Product) + .Include(x => x.Orders) + .ThenInclude(x => x.Order) + .Where(x => + (model.Id.HasValue && x.Id == model.Id) || + (model.ManagerId.HasValue && model.ManagerId == x.ManagerId) || + (model.OrderId.HasValue && x.Orders.Find(x => x.CookId == model.OrderId) != null)) .Select(x => x.GetViewModel) .ToList(); } diff --git a/Canteen/CanteenDatabaseImplement/Implements/LunchStorage.cs b/Canteen/CanteenDatabaseImplement/Implements/LunchStorage.cs index 4afa0e3..a244d82 100644 --- a/Canteen/CanteenDatabaseImplement/Implements/LunchStorage.cs +++ b/Canteen/CanteenDatabaseImplement/Implements/LunchStorage.cs @@ -55,11 +55,11 @@ namespace CanteenDatabaseImplement.Implements return context.Lunches .Include(x => x.Orders) - .ThenInclude(x => x.Order) + .ThenInclude(x => x.Order) .Include(x => x.Products) - .ThenInclude(x => x.Product) - .ThenInclude(x => x.Cooks) - .ThenInclude(x => x.Cook) + .ThenInclude(x => x.Product) + .ThenInclude(x => x.Cooks) + .ThenInclude(x => x.Cook) .Where(x => (x.DateCreate >= model.DateFrom && x.DateImplement <= model.DateTo) || (model.Id.HasValue && x.Id == model.Id) || diff --git a/Canteen/CanteenDatabaseImplement/Implements/OrderStorage.cs b/Canteen/CanteenDatabaseImplement/Implements/OrderStorage.cs index 8a186f2..d51488e 100644 --- a/Canteen/CanteenDatabaseImplement/Implements/OrderStorage.cs +++ b/Canteen/CanteenDatabaseImplement/Implements/OrderStorage.cs @@ -5,6 +5,7 @@ using CanteenContracts.StoragesContracts; using CanteenContracts.View; using CanteenContracts.ViewModels; using CanteenDatabaseImplement.Models; +using DocumentFormat.OpenXml.Office2019.Drawing.Model3D; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; @@ -16,17 +17,17 @@ namespace CanteenDatabaseImplement.Implements { public class OrderStorage : IOrderStorage { - public OrderCookViewModel? GetOrderCookElement(OrderCookSearchModel model) + public List? GetOrderCooksList(OrderSearchModel model) { - if (!model.Id.HasValue && (!model.OrderId.HasValue || !model.CookId.HasValue)) + if (!model.CookId.HasValue) { return null; } using var context = new CanteenDatabase(); - return context.OrderCook - .FirstOrDefault(x => x.OrderId == model.OrderId && x.CookId == model.CookId)?.GetViewModel; + var orders = context.Orders.Include(x => x.Cooks).ThenInclude(x => x.Cook).Select(x => x.GetViewModel).ToList(); + return orders.Where(x => x.OrderCooks.ContainsKey((int)model.CookId)).ToList(); } public OrderViewModel? GetElement(OrderSearchModel model) @@ -61,7 +62,10 @@ namespace CanteenDatabaseImplement.Implements .ThenInclude(x => x.Cook) .Include(x => x.Tablewares) .ThenInclude(x => x.Tableware) - .Where(x => (model.Id.HasValue && x.Id == model.Id) || (model.VisitorId.HasValue && model.VisitorId == x.VisitorId)).Select(x => x.GetViewModel).ToList(); + .Where(x => + (model.Id.HasValue && x.Id == model.Id) || + (model.VisitorId.HasValue && model.VisitorId == x.VisitorId)) + .Select(x => x.GetViewModel).ToList(); } public List GetFullList() diff --git a/Canteen/CanteenDatabaseImplement/Implements/ProductStorage.cs b/Canteen/CanteenDatabaseImplement/Implements/ProductStorage.cs index 1d818be..ab9e013 100644 --- a/Canteen/CanteenDatabaseImplement/Implements/ProductStorage.cs +++ b/Canteen/CanteenDatabaseImplement/Implements/ProductStorage.cs @@ -52,11 +52,11 @@ namespace CanteenDatabaseImplement.Implements } using var context = new CanteenDatabase(); - return context.Products .Include(x => x.Cooks) .ThenInclude(x => x.Cook) - .Where(x => ((model.Id.HasValue && x.Id == model.Id) || (model.ManagerId.HasValue && model.ManagerId == x.ManagerId))) + .Where(x => (model.Id.HasValue && x.Id == model.Id) || + (model.ManagerId.HasValue && model.ManagerId == x.ManagerId)) .Select(x => x.GetViewModel).ToList(); } diff --git a/Canteen/CanteenDatabaseImplement/Models/Cook.cs b/Canteen/CanteenDatabaseImplement/Models/Cook.cs index e9493f2..b83e493 100644 --- a/Canteen/CanteenDatabaseImplement/Models/Cook.cs +++ b/Canteen/CanteenDatabaseImplement/Models/Cook.cs @@ -58,7 +58,7 @@ namespace CanteenDatabaseImplement.Models Id = Id, ManagerId = ManagerId, FIO = FIO, - Position = Position + Position = Position, }; } } diff --git a/Canteen/CanteenManagerApp/Controllers/HomeController.cs b/Canteen/CanteenManagerApp/Controllers/HomeController.cs index c7dac2f..b8826fe 100644 --- a/Canteen/CanteenManagerApp/Controllers/HomeController.cs +++ b/Canteen/CanteenManagerApp/Controllers/HomeController.cs @@ -474,5 +474,40 @@ namespace CanteenManagerApp.Controllers ViewBag.Model = APIClient.GetRequest>($"api/main/GetGraphic"); return View(); } + [HttpGet] + public IActionResult Report() + { + return View(new ReportBindingModel()); + } + [HttpPost] + public void ReportPdf(ReportBindingModel model) + { + model.UserId = APIClient.Manager.Id; + APIClient.PostRequest("api/main/SaveLunchesToPDF", model); + Response.Redirect("Index"); + } + + [HttpPost] + public void ReportXsl(ReportBindingModel model) + { + model.UserId = APIClient.Manager.Id; + APIClient.PostRequest("api/main/SaveOrdersToXSL", model); + Response.Redirect("Index"); + } + + [HttpPost] + public void ReportWord(ReportBindingModel model) + { + model.UserId = APIClient.Manager.Id; + APIClient.PostRequest("api/main/SaveOrdersToWORD", model); + Response.Redirect("Index"); + } + + [HttpPost] + public void ReportEmail(ReportBindingModel model) + { + APIClient.PostRequest("api/main/SaveEMAIL", model); + Response.Redirect("Index"); + } } } \ No newline at end of file diff --git a/Canteen/CanteenManagerApp/Views/Home/Report.cshtml b/Canteen/CanteenManagerApp/Views/Home/Report.cshtml new file mode 100644 index 0000000..f34fc8b --- /dev/null +++ b/Canteen/CanteenManagerApp/Views/Home/Report.cshtml @@ -0,0 +1,31 @@ +@using CanteenContracts.BindingModels; +@model ReportBindingModel + +@{ + ViewBag.Title = "Report"; +} + +

Generate Report

+ +@using (Html.BeginForm("Report", "Home", FormMethod.Post)) +{ +
+ @Html.LabelFor(m => m.FileName) + @Html.TextBoxFor(m => m.FileName) +
+ +
+ @Html.LabelFor(m => m.DateAfter) + @Html.TextBoxFor(m => m.DateAfter, new { type = "date" }) +
+ +
+ @Html.LabelFor(m => m.DateBefore) + @Html.TextBoxFor(m => m.DateBefore, new { type = "date" }) +
+ + + + + +} \ No newline at end of file diff --git a/Canteen/CanteenManagerApp/Views/Shared/_Layout.cshtml b/Canteen/CanteenManagerApp/Views/Shared/_Layout.cshtml index f65f83e..832a05f 100644 --- a/Canteen/CanteenManagerApp/Views/Shared/_Layout.cshtml +++ b/Canteen/CanteenManagerApp/Views/Shared/_Layout.cshtml @@ -34,6 +34,9 @@ + diff --git a/Canteen/CanteenRestApi/Controllers/MainController.cs b/Canteen/CanteenRestApi/Controllers/MainController.cs index d3fed55..0b520ba 100644 --- a/Canteen/CanteenRestApi/Controllers/MainController.cs +++ b/Canteen/CanteenRestApi/Controllers/MainController.cs @@ -37,7 +37,7 @@ namespace CanteenRestApi.Controllers } [HttpPost] - public void SavePDF(ReportBindingModel model) + public void SaveLunchesToPDF(ReportBindingModel model) { try { @@ -46,8 +46,8 @@ namespace CanteenRestApi.Controllers DateAfter = model.DateAfter, DateBefore = model.DateBefore, FileName = model.FileName, - VisitorId = model.VisitorId, - lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.VisitorId}), + UserId = model.UserId, + lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.UserId}), }); } catch (Exception ex) @@ -58,7 +58,7 @@ namespace CanteenRestApi.Controllers } [HttpPost] - public IActionResult SaveXSL(ReportBindingModel model) + public void SaveCooksToXSL(ReportBindingModel model) { try { @@ -70,12 +70,9 @@ namespace CanteenRestApi.Controllers DateAfter = model.DateAfter, DateBefore = model.DateBefore, FileName = excelFilePath, - VisitorId = model.VisitorId, - lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.VisitorId }), + UserId = model.UserId, + lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.UserId }), }); - - byte[] fileBytes = System.IO.File.ReadAllBytes(excelFilePath); - return File(fileBytes, "application/octet-stream", excelFileName); } catch (Exception ex) { @@ -85,7 +82,7 @@ namespace CanteenRestApi.Controllers } [HttpPost] - public void SaveWORD(ReportBindingModel model) + public void SaveCooksToWORD(ReportBindingModel model) { try { @@ -94,8 +91,47 @@ namespace CanteenRestApi.Controllers DateAfter = model.DateAfter, DateBefore = model.DateBefore, FileName = model.FileName, - VisitorId = model.VisitorId, - lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.VisitorId }), + UserId = model.UserId, + lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.UserId }), + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error during loading list of bouquets"); + throw; + } + } + + [HttpPost] + public void SaveOrdersToXSL(ReportBindingModel model) + { + try + { + _reportLogic.saveOrdersToExcel(new ReportBindingModel() + { + FileName = $"{model.FileName}.xlsx", + UserId = model.UserId, + orders = _order.ReadList(new OrderSearchModel { VisitorId = model.UserId }), + }); + + } + catch (Exception ex) + { + _logger.LogError(ex, "Error during loading list of bouquets"); + throw; + } + } + + [HttpPost] + public void SaveOrderToWORD(ReportBindingModel model) + { + try + { + _reportLogic.saveOrdersToWord(new ReportBindingModel() + { + FileName = $"{model.FileName}.docx", + UserId = model.UserId, + lunches = _lunch.ReadList(new LunchSearchModel { VisitorId = model.UserId }), }); } catch (Exception ex) diff --git a/Canteen/CanteenRestApi/Cook.docx b/Canteen/CanteenRestApi/Cook.docx new file mode 100644 index 0000000000000000000000000000000000000000..09b976c87887946765415f324c0388d0062c1d67 GIT binary patch literal 1454 zcmWIWW@Zs#VBp|jNbcDhX0e-p{4CVPnDf%h-$)&lec_n%kxjApc z?)u#}5UCSA{XN?&sxD>XC$7uiSQHI&1Crdg-QEy;@Yq=s6^BP$iWlxFD69%KIHfJK z|6-qT7h8_@J7r*Ei7TF)Wv!Yz>z1@Ugv#sa7y&lWDIwO4ffde}q&05X4T=oasc7sjU z25s6+uO##qY`N{X>+G_&6^H-Le7VbHujKPeuIC0V-fnUiW;NN#eiJxWcDwGU_=V?k zGY<$Zdn7z%iP!n^9W6q629dw@eGVO;7n$?JcfYIHw211NTcbZu{k{43CO!GsyK91G zpL@x$F0tilfY2I89pT@i;_F^5wOm-y!u$GFZTo@FzcHos$==Q;C^aXs1gN4oBeS4b z0jMRf*j6c{q@=(~U%xmxBQ-a%STDaIH4iA0mS2>cSOVl0rRx_YCTAz6r|KIS7?|k; zZA6%%gleWjNM%7PF(&4xrDZ0kx)r65!D z#8l&C6O$D4L`#ztqckN2eLEX{6d&862I+lirtm0WaDuQfFi4}F^Yco8*(o06i{e;N zt|C5Y!FtFH(qxFex+NfcfkEn$nwD6aQ=;Hn0dx)|MuBcoQh>S-63B@K1v#0?;Hc0q z%S%CvH(f~h!Al5Gs1+3DC#Mz{1CvBz>% literal 0 HcmV?d00001 diff --git a/Canteen/CanteenRestApi/Cook.docx.xlsx.xls b/Canteen/CanteenRestApi/Cook.docx.xlsx.xls new file mode 100644 index 0000000000000000000000000000000000000000..dd56d272b3a9aeae17d9970a9c3b06d7fcef33e4 GIT binary patch literal 3056 zcma);2UHX37RQ4~2vwSZNC}|`QW82W1f+v3B}fet5;g}UBGML>CZUKR z2vVa+SY#pVA}R<{Hy}ks7Z8-<8}#h5@Z57==FH5=$#?&g?>G0}|8;TT+$#nGK_H;^ zmO76Q9>eurAP`8I0|eR!+@nL_QDjQ+2{JiYGdeURCC!}j7{PB{HPOo$K%C{tQ4ATN zbqo9m6Ww?3`aOPDCZH&leC4+d3^x`!uD zDI~c2rglhN+{g@oKWlw+;!ghM2$f=Xi6uS8g7foz9kIOBKz`#c%Ok^-1W8{w>8M6= z-^=!mQH4nfo+=rS@K23-RrEQ7MBC4=l$C@Es(JJqbMZv9-i7MMMA$dip*_n6MLRn` zuH1O~W;MI^a;nIJV3IgBb8SrVPbBjw23OD8XuWg3@pMG|OG2fI5UttLE;fiexK?u| z%2nT6vVKw!HE{DiSWC~7YF^-Gc~4--2I^H8dr%1{ZC!iCqUWa+FRHRn6+2FuYWA^J zP_ep)p%ed<7kRtzL!2gZ`9j)+>-)_9Ka1JA?8p>{rHlw+O$#roxJX0%0DD6>CX!ohc+9-mZ z5^?{m%k{Ndc1rC5^c0HOd;wd!))$ur5&NJRxFB+FGQQ+ExKE6f1ZjK`5nqwO%T;30 zJcy4&yL_^@E|+R%c(;}v9m)~^RY1L0tcISQe~TgBT5*Hp=S$MGEQ3(hp9G{=74i;f zupVcPw~g9Vv_ac^Y=p0kr1Guj6KTe3*E!4LOEAZhy35{R9uZTT%qxGGxGU=vOL;Q& zyBs0neO%M%)MiS&UzcJFOI9@L)q+lGfqAa`a5f`A{nb*W5?`UvnHdM@xXp%)G*^|^ zVYhbV)j6CiQtbT30rzOJ`%Qv+}mg8wlZB`}QiwHU6aItH~Pgf3RW?w?c` zvUd=!uYITW;~nNDJN-;_OOJCF(p*HP@vN$bs-~i+4SXq6DI+U3y$x5X ze<}v~W*jbQdqlS?wa?ifB;IGM)7nV?Louk~<)wW0z!9ddLCHk&jJ>9RIX6K`jN#ag zE&&se_@;8}+?nGLg*WO;8^+3g7QIxr@p60YzC)|`9SkjO$3PmiXhD_HXl7pfi_2@j zn)sXDnD^6PKLD1rPi6sITLN^ zO_E&R+d<6;(%jYFQiDd@$j#AtGShcZadamqul4s5TmU0UPT>5;X{$$40|*2f6+i~u zw6+?@sb@oB7obAmF;Dr|Q798Vt6^OqxeE=^c3k|w`^r7!ln_v69t>YylozaDu?(A5 zO7^Xr4Ug|ehy*=<>duncjO^+DXgHRIq(K%xsdQ~utQgjS!F0r2nNcV_Ix)1C&U--X(?P<%fsEq9a{|AA@OP`>Xv#@Gtbc@_j$(nQEwqCMKz&}iX5z~NMU03j4d)g*@# z!hm0hWJ)NG27INE;NduYFpflkYaK=GL#TT z`;IVt=L#6q4M!mnXh;}*m#@O0_WnqiE8YNSq^(Uf)b-cV)zvn@!=P|e6Znq(n*Ig! zn_s^`v)(KNAdds?CG{;AzcKx%Yk%cimu~I^RIYW^3D(U4@B>FwU)TW6C-!U^(y37S zq<`~m}-96qAEc?DeWrdodQk>^w zV(TC(L3K3I(by{UR)d%MwP)|2#5@;RmgF+6M%sUhOn2O(bg@b#=JE-; zx{!r^lS&8&k0#dW+Arl_*f|+XA?k)Ap4i+%h-!& z0Q0Cv;Th}+QhU9y!t!LL*MsFbc@FW{Qq{tosHS7QED)wBnP-pZ=j>MHk*Ug~gKw9_ zpGUe6tB7)%=clL27FI44^cNLP;3obM&73S8$orgHYBQaYrU5TP#ZujdSXZz)oN9|VF-LJP56uJJMaQ~-! zZ>xO2b#1Hofp;{r?ZEGs=yqT=_t(e&OTq5e*`2-HI>V6f=CX?e7x2$OAa3AzyyaKG IbPEFg7iD1ZY5)KL literal 0 HcmV?d00001 diff --git a/Canteen/CanteenRestApi/Cook.xlsx b/Canteen/CanteenRestApi/Cook.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1f8d8d7e83bae20013be1a1d4195d03f01b760a1 GIT binary patch literal 3139 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