PIbd-21. Razubaev SM Lab work 5 hard #13

Closed
Sergey wants to merge 13 commits from lab_5_hard into lab_5
149 changed files with 79108 additions and 67 deletions

View File

@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlumbingRepairDatabaseImple
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlumbingRepairClientApp", "..\..\PlumbingRepair 5 база\PlumbingRepairClientApp\PlumbingRepairClientApp.csproj", "{9A10B8D9-1C83-4A71-9517-F39C22C67DE5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlumbingRepairRestApi", "..\..\PlumbingRepair 5 база\PlumbingRepairRestApi\PlumbingRepairRestApi.csproj", "{45E8C718-AABF-48E1-8784-158EFED300A3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlumbingRepairShopApp", "PlumbingRepairShopApp\PlumbingRepairShopApp.csproj", "{4CD222EE-61C1-43D7-85DB-953094F66874}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlumbingRepairRestApi", "PlumbingRepairRestApi\PlumbingRepairRestApi.csproj", "{D8850677-4C7F-4E24-96F7-F6EF8A19C203}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -59,10 +61,14 @@ Global
{9A10B8D9-1C83-4A71-9517-F39C22C67DE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A10B8D9-1C83-4A71-9517-F39C22C67DE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A10B8D9-1C83-4A71-9517-F39C22C67DE5}.Release|Any CPU.Build.0 = Release|Any CPU
{45E8C718-AABF-48E1-8784-158EFED300A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45E8C718-AABF-48E1-8784-158EFED300A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45E8C718-AABF-48E1-8784-158EFED300A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45E8C718-AABF-48E1-8784-158EFED300A3}.Release|Any CPU.Build.0 = Release|Any CPU
{4CD222EE-61C1-43D7-85DB-953094F66874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4CD222EE-61C1-43D7-85DB-953094F66874}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4CD222EE-61C1-43D7-85DB-953094F66874}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4CD222EE-61C1-43D7-85DB-953094F66874}.Release|Any CPU.Build.0 = Release|Any CPU
{D8850677-4C7F-4E24-96F7-F6EF8A19C203}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D8850677-4C7F-4E24-96F7-F6EF8A19C203}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D8850677-4C7F-4E24-96F7-F6EF8A19C203}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D8850677-4C7F-4E24-96F7-F6EF8A19C203}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -14,10 +14,16 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
private readonly ILogger _logger;
private readonly IOrderStorage _orderStorage;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage)
private readonly IShopStorage _shopStorage;
private readonly IWorkStorage _workStorage;
private readonly IShopLogic _shopLogic;
public OrderLogic(ILogger<OrderLogic> logger, IOrderStorage orderStorage, IShopStorage shopStorage, IWorkStorage workStorage, IShopLogic shopLogic)
{
_logger = logger;
_orderStorage = orderStorage;
_shopStorage = shopStorage;
_workStorage = workStorage;
_shopLogic = shopLogic;
}
public bool CreateOrder(OrderBindingModel model)
{
@ -75,11 +81,21 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
_logger.LogWarning("Change status operation failed");
return false;
}
if (newStatus == OrderStatus.Выдан)
{
var work = _workStorage.GetElement(new WorkSearchModel() { Id = viewModel.WorkId});
if (work == null)
{
_logger.LogWarning("Change status operation failed.Work not found");
return false;
}
if (!_shopLogic.CheckAndSupply(work, viewModel.Count))
{
_logger.LogWarning("Change status operation failed. Works delivery operation failed");
return false;
}
}
model.Status = newStatus;
model.WorkId = viewModel.WorkId;
model.Count = viewModel.Count;
model.Sum = viewModel.Sum;
model.DateCreate = viewModel.DateCreate;
if (model.Status == OrderStatus.Готов)
{
model.DateImplement = DateTime.Now;

View File

@ -15,6 +15,7 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
private readonly IWorkStorage _workStorage;
private readonly IOrderStorage _orderStorage;
private readonly IShopStorage _shopStorage;
private readonly AbstractSaveToExcel _saveToExcel;
@ -22,16 +23,17 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
private readonly AbstractSaveToPdf _saveToPdf;
public ReportLogic(IWorkStorage workStorage, IComponentStorage componentStorage, IOrderStorage orderStorage,
public ReportLogic(IWorkStorage workStorage, IComponentStorage componentStorage, IOrderStorage orderStorage, IShopStorage shopStorage,
AbstractSaveToExcel saveToExcel, AbstractSaveToWord saveToWord, AbstractSaveToPdf saveToPdf)
{
_workStorage = workStorage;
_componentStorage = componentStorage;
_orderStorage = orderStorage;
_shopStorage = shopStorage;
_saveToExcel = saveToExcel;
_saveToWord = saveToWord;
_saveToPdf = saveToPdf;
_saveToPdf = saveToPdf;
}
/// <summary>
@ -65,6 +67,33 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
return list;
}
public List<ReportShopWorkViewModel> GetShopWork()
{
var shops = _shopStorage.GetFullList();
var list = new List<ReportShopWorkViewModel>();
foreach (var shop in shops)
{
var record = new ReportShopWorkViewModel
{
ShopName = shop.ShopName,
Works = new List<(string Work, int count)>(),
TotalCount = 0
};
foreach (var work in shop.ShopWorks)
{
record.Works.Add(new(work.Value.Item1.WorkName, work.Value.Item2));
record.TotalCount += work.Value.Item2;
}
list.Add(record);
}
return list;
}
/// <summary>
/// Получение списка заказов за определенный период
/// </summary>
@ -84,6 +113,19 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
.ToList();
}
public List<ReportOrderByDateViewModel> GetOrdersByDate()
{
return _orderStorage.GetFullList()
.GroupBy(x => x.DateCreate.Date)
.Select(x => new ReportOrderByDateViewModel
{
DateCreate = x.Key,
Count = x.Count(),
Sum = x.Sum(x=> x.Sum)
})
.ToList();
}
/// <summary>
/// Сохранение компонент в файл-Word
/// </summary>
@ -97,6 +139,15 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
Works = _workStorage.GetFullList()
});
}
public void SaveShopsToWordFile(ReportBindingModel model)
{
_saveToWord.CreateDocShopTable(new WordInfoShopTable
{
FileName = model.FileName,
Title = "Список магазинов",
Shops = _shopStorage.GetFullList()
});
}
/// <summary>
/// Сохранение компонент с указаеним продуктов в файл-Excel
@ -111,6 +162,15 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
WorkComponents = GetWorkComponent()
});
}
public void SaveShopWorkToExcelFile(ReportBindingModel model)
{
_saveToExcel.CreateReportShop(new ExcelInfoShop
{
FileName = model.FileName,
Title = "Список магазинов",
ShopWorks = GetShopWork()
});
}
/// <summary>
/// Сохранение заказов в файл-Pdf
@ -127,5 +187,14 @@ namespace PlumbingRepairBusinessLogic.BusinessLogics
Orders = GetOrders(model)
});
}
public void SaveOrdersByDateToPdfFile(ReportBindingModel model)
{
_saveToPdf.CreateDocOrdersByDate(new PdfInfoOrdersByDate
{
FileName = model.FileName,
Title = "Список заказов",
Orders = GetOrdersByDate()
});
}
}
}

View File

@ -0,0 +1,230 @@
using Microsoft.Extensions.Logging;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace PlumbingRepairBusinessLogic.BusinessLogics
{
public class ShopLogic :IShopLogic
{
private readonly ILogger _logger;
private readonly IShopStorage _shopStorage;
public ShopLogic(ILogger<ShopLogic> logger, IShopStorage shopStorage)
{
_logger = logger;
_shopStorage = shopStorage;
}
public List<ShopViewModel>? ReadList(ShopSearchModel? model)
{
_logger.LogInformation("ReadList. ShopName:{ShopName}. Id:{Id}", model?.ShopName, model?.Id);
var list = model == null ? _shopStorage.GetFullList() : _shopStorage.GetFilteredList(model);
if (list == null)
{
_logger.LogWarning("ReadList return null list");
return null;
}
_logger.LogInformation("ReadList. Count:{Count}", list.Count);
return list;
}
public ShopViewModel? ReadElement(ShopSearchModel model)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
_logger.LogInformation("ReadElement. ShopName:{ShopName}. Id:{Id}", model.ShopName, model.Id);
var element = _shopStorage.GetElement(model);
if (element == null)
{
_logger.LogWarning("ReadElement element not found");
return null;
}
_logger.LogInformation("ReadElement find. Id:{Id}", element.Id);
return element;
}
public bool Create(ShopBindingModel model)
{
CheckModel(model);
if (_shopStorage.Insert(model) == null)
{
_logger.LogWarning("Insert operation failed");
return false;
}
return true;
}
public bool Update(ShopBindingModel model)
{
CheckModel(model);
if (_shopStorage.Update(model) == null)
{
_logger.LogWarning("Update operation failed");
return false;
}
return true;
}
public bool Delete(ShopBindingModel model)
{
CheckModel(model, false);
_logger.LogInformation("Delete. Id:{Id}", model.Id);
if (_shopStorage.Delete(model) == null)
{
_logger.LogWarning("Delete operation failed");
return false;
}
return true;
}
public bool StoreReplenishment(ShopSearchModel shopModel, IWorkModel workModel, int count)
{
if (shopModel == null)
{
throw new ArgumentNullException(nameof(shopModel));
}
if (workModel == null)
{
throw new ArgumentNullException(nameof(workModel));
}
if (count <= 0)
{
throw new ArgumentNullException("Количество товаров должно быть больше 0", nameof(count));
}
_logger.LogInformation("StoreReplenishment. ShopName:{ShopName}. Id:{Id}", shopModel.ShopName, shopModel.Id);
var element = _shopStorage.GetElement(shopModel);
if (element == null)
{
throw new InvalidOperationException("Ошибка. Элемент не найден");
}
if(element.maxCountWorks - element.ShopWorks.Sum(x => x.Value.Item2) < count)
{
throw new InvalidOperationException("Ошибка. Нет места в магазине");
}
if(element.ShopWorks.ContainsKey(workModel.Id))
{
var oldWorks = element.ShopWorks[workModel.Id];
oldWorks.Item2 += count;
element.ShopWorks[workModel.Id] = oldWorks;
_logger.LogInformation("StoreReplenishment. Added {count} '{work}' to '{ShopName}' shop", count, workModel.WorkName,shopModel.ShopName);
}
else
{
element.ShopWorks.Add(workModel.Id,(workModel, count));
_logger.LogInformation("StoreReplenishment. Added {count} new '{work}' to '{ShopName}' shop", count, workModel.WorkName, shopModel.ShopName);
}
if(_shopStorage.Update(new ShopBindingModel()
{
Id = element.Id,
ShopName = element.ShopName,
Address = element.Address,
DateOpening = element.DateOpening,
maxCountWorks = element.maxCountWorks,
ShopWorks= element.ShopWorks,
}) == null)
{
_logger.LogInformation("StoreReplenishment. Update operation failed");
return false;
}
return true;
}
public bool CheckAndSupply(IWorkModel model, int count)
{
if (count<0)
{
_logger.LogWarning("Checksupply operation error. Works count < 0.");
return false;
}
var shopList = _shopStorage.GetFullList();
int shopsCapacity = shopList.Sum(x => x.maxCountWorks);
int currentWorks = shopList.Select(x => x.ShopWorks.Sum(x => x.Value.Item2)).Sum();
int freespace = shopsCapacity - currentWorks;
if ( freespace < count)
{
_logger.LogWarning("Checksupply operation error. No space for a new work.");
return false;
}
foreach (var shop in shopList)
{
int freePlaces = shop.maxCountWorks - shop.ShopWorks.Select(x => x.Value.Item2).Sum();
if (freePlaces == 0)
{
continue;
}
if( freePlaces - count >= 0)
{
if (StoreReplenishment(new() { Id = shop.Id }, model, count))
count = 0;
else
{
_logger.LogWarning("Supply error");
return false;
}
}
if (freePlaces - count < 0)
{
if (StoreReplenishment(new() { Id = shop.Id }, model, freePlaces))
count-= freePlaces;
else
{
_logger.LogWarning("Supply error");
return false;
}
}
if (count == 0)
return true;
}
return false;
}
private void CheckModel(ShopBindingModel model, bool withParams = true)
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
if (!withParams)
{
return;
}
if (string.IsNullOrEmpty(model.ShopName))
{
throw new ArgumentNullException("Нет названия магазина", nameof(model.ShopName));
}
if (string.IsNullOrEmpty(model.Address))
{
throw new ArgumentNullException("Нет адреса магазина", nameof(model.ShopName));
}
if (model.maxCountWorks < 0)
{
throw new InvalidOperationException("Максимальное количество работ магазина отрицательно");
}
_logger.LogInformation("Shop. ShopName:{ShopName}. Address:{Address}. Id:{Id}", model.ShopName, model.Address, model.Id);
var element = _shopStorage.GetElement(new ShopSearchModel
{
ShopName = model.ShopName
});
if (element != null && element.Id != model.Id)
{
throw new InvalidOperationException("Компонент с таким названием уже есть");
}
}
public bool SellWork(IWorkModel model, int count)
{
return _shopStorage.SellWork(model, count);
}
}
}

View File

@ -80,11 +80,85 @@ namespace PlumbingRepairBusinessLogic.OfficePackage
SaveExcel(info);
}
/// <summary>
/// Создание excel-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateExcel(ExcelInfo info);
/// <summary>
/// Создание отчета по магазинам
/// </summary>
/// <param name="info"></param>
public void CreateReportShop(ExcelInfoShop info)
{
CreateExcel(new ExcelInfo() { FileName = info.FileName});
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 pc in info.ShopWorks)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = pc.ShopName,
StyleInfo = ExcelStyleInfoType.Text
});
rowIndex++;
foreach (var (Work, Count) in pc.Works)
{
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "B",
RowIndex = rowIndex,
Text = Work,
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = Count.ToString(),
StyleInfo = ExcelStyleInfoType.TextWithBroder
});
rowIndex++;
}
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "A",
RowIndex = rowIndex,
Text = "Итого",
StyleInfo = ExcelStyleInfoType.Text
});
InsertCellInWorksheet(new ExcelCellParameters
{
ColumnName = "C",
RowIndex = rowIndex,
Text = pc.TotalCount.ToString(),
StyleInfo = ExcelStyleInfoType.Text
});
rowIndex++;
}
SaveExcel(new ExcelInfo() { FileName = info.FileName });
}
/// <summary>
/// Создание excel-файла
/// </summary>
/// <param name="info"></param>
protected abstract void CreateExcel(ExcelInfo info);
/// <summary>
/// Добавляем новую ячейку в лист

View File

@ -33,6 +33,34 @@ namespace PlumbingRepairBusinessLogic.OfficePackage
SavePdf(info);
}
public void CreateDocOrdersByDate(PdfInfoOrdersByDate info)
{
CreatePdf(new PdfInfo() { FileName = info.FileName});
CreateParagraph(new PdfParagraph { Text = info.Title, Style = "NormalTitle", ParagraphAlignment = PdfParagraphAlignmentType.Center });
CreateTable(new List<string> { "3cm", "3cm", "3cm"});
CreateRow(new PdfRowParameters
{
Texts = new List<string> { "Дата", "Количество", "Сумма"},
Style = "NormalTitle",
ParagraphAlignment = PdfParagraphAlignmentType.Center
});
foreach (var order in info.Orders)
{
CreateRow(new PdfRowParameters
{
Texts = new List<string> { order.DateCreate.ToShortDateString(), order.Count.ToString(), order.Sum.ToString()},
Style = "Normal",
ParagraphAlignment = PdfParagraphAlignmentType.Left
});
}
CreateParagraph(new PdfParagraph { Text = $"Итого: {info.Orders.Sum(x => x.Sum)}\t", Style = "Normal", ParagraphAlignment = PdfParagraphAlignmentType.Right });
SavePdf(new PdfInfo() { FileName = info.FileName});
}
/// <summary>
/// Создание doc-файла

View File

@ -34,7 +34,36 @@ namespace PlumbingRepairBusinessLogic.OfficePackage
SaveWord(info);
}
public void CreateDocShopTable(WordInfoShopTable info)
{
CreateWord(new WordInfo() { FileName = info.FileName});
CreateParagraph(new WordParagraph
{
Texts = new List<(string, WordTextProperties)> { (info.Title, new WordTextProperties { Bold = true, Size = "24", }) },
TextProperties = new WordTextProperties
{
Size = "24",
JustificationType = WordJustificationType.Center
}
});
CreateTable(new WordTable
{
Columns = new() { ("Название",3000),("Адрес",3000),("Дата открытия",3000)},
Rows = info.Shops.Select(x => new List<string>
{
x.ShopName,
x.Address,
Convert.ToString(x.DateOpening),
})
.ToList()
});;
SaveWord(new WordInfo() { FileName = info.FileName});
}
/// <summary>
/// Создание doc-файла
/// </summary>
@ -47,6 +76,7 @@ namespace PlumbingRepairBusinessLogic.OfficePackage
/// <param name="paragraph"></param>
/// <returns></returns>
protected abstract void CreateParagraph(WordParagraph paragraph);
protected abstract void CreateTable(WordTable table);
/// <summary>
/// Сохранение файла

View File

@ -0,0 +1,13 @@
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairBusinessLogic.OfficePackage.HelperModels
{
public class ExcelInfoShop
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ReportShopWorkViewModel> ShopWorks { get; set; } = new();
}
}

View File

@ -0,0 +1,12 @@
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairBusinessLogic.OfficePackage.HelperModels
{
public class PdfInfoOrdersByDate
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ReportOrderByDateViewModel> Orders { get; set; } = new();
}
}

View File

@ -0,0 +1,13 @@
using PlumbingRepairContracts.ViewModels;
namespace PlumbingRepairBusinessLogic.OfficePackage.HelperModels
{
public class WordInfoShopTable
{
public string FileName { get; set; } = string.Empty;
public string Title { get; set; } = string.Empty;
public List<ShopViewModel> Shops { get; set; } = new();
}
}

View File

@ -0,0 +1,8 @@
namespace PlumbingRepairBusinessLogic.OfficePackage.HelperModels
{
public class WordTable
{
public List<(string, int)> Columns { get; set; } = new();
public List<List<string>> Rows { get; set; } = new();
}
}

View File

@ -119,6 +119,40 @@ namespace PlumbingRepairBusinessLogic.OfficePackage.Implements
_docBody.AppendChild(docParagraph);
}
private static TableCell CreateTableCell(string text, int? cellWidth = null)
{
TableCell tableCell = new(new Paragraph(new Run(new Text(text))));
tableCell.Append(new TableCellProperties()
{
TableCellWidth = new() { Width = cellWidth.ToString() }
});
return tableCell;
}
protected override void CreateTable(WordTable table)
{
if (_docBody == null || table == null)
{
return;
}
var docTable = new Table();
TableProperties tableProperties = new(
new TableBorders(
new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 },
new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 },
new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 },
new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 },
new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 },
new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.BasicBlackDashes), Size = 3 }
)
);
docTable.AppendChild(tableProperties);
docTable.Append(new TableRow(table.Columns.Select(x => CreateTableCell(x.Item1, x.Item2))));
docTable.Append(table.Rows.Select(x => new TableRow(x.Select(y => CreateTableCell(y)))));
_docBody.AppendChild(docTable);
}
protected override void SaveWord(WordInfo info)
{
if (_docBody == null || _wordDocument == null)

View File

@ -0,0 +1,25 @@
using PlumbingRepairDataModels.Enums;
using PlumbingRepairDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlumbingRepairContracts.BindingModels
{
public class ShopBindingModel
{
public int Id { get; set; }
public string ShopName { get; set; } =string.Empty;
public string Address { get; set; } = string.Empty;
public int maxCountWorks { get; set; }
public DateTime DateOpening { get; set; } = DateTime.Now;
public Dictionary<int, (IWorkModel, int)> ShopWorks { get; set; } = new();
}
}

View File

@ -10,6 +10,7 @@ namespace PlumbingRepairContracts.BusinessLogicsContracts
/// </summary>
/// <returns></returns>
List<ReportWorkComponentViewModel> GetWorkComponent();
List<ReportShopWorkViewModel> GetShopWork();
/// <summary>
/// Получение списка заказов за определенный период
@ -17,6 +18,8 @@ namespace PlumbingRepairContracts.BusinessLogicsContracts
/// <param name="model"></param>
/// <returns></returns>
List<ReportOrdersViewModel> GetOrders(ReportBindingModel model);
List<ReportOrderByDateViewModel> GetOrdersByDate();
/// <summary>
/// Сохранение компонент в файл-Word
@ -35,5 +38,9 @@ namespace PlumbingRepairContracts.BusinessLogicsContracts
/// </summary>
/// <param name="model"></param>
void SaveOrdersToPdfFile(ReportBindingModel model);
void SaveShopsToWordFile(ReportBindingModel model);
void SaveShopWorkToExcelFile(ReportBindingModel model);
public void SaveOrdersByDateToPdfFile(ReportBindingModel model);
}
}

View File

@ -0,0 +1,30 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlumbingRepairContracts.BusinessLogicsContracts
{
public interface IShopLogic
{
List<ShopViewModel>? ReadList(ShopSearchModel? model);
ShopViewModel? ReadElement(ShopSearchModel model);
bool Create(ShopBindingModel model);
bool Update(ShopBindingModel model);
bool Delete(ShopBindingModel model);
bool StoreReplenishment(ShopSearchModel shopModel, IWorkModel workModel, int count);
bool SellWork(IWorkModel workModel, int count);
bool CheckAndSupply(IWorkModel workModel, int count);
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlumbingRepairContracts.SearchModels
{
public class ShopSearchModel
{
public int? Id { get; set; }
public string? ShopName { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairContracts.StoragesContracts
{
public interface IShopStorage
{
List<ShopViewModel> GetFullList();
List<ShopViewModel> GetFilteredList(ShopSearchModel model);
ShopViewModel? GetElement(ShopSearchModel model);
ShopViewModel? Insert(ShopBindingModel model);
ShopViewModel? Update(ShopBindingModel model);
ShopViewModel? Delete(ShopBindingModel model);
bool SellWork(IWorkModel workModel, int count);
}
}

View File

@ -8,6 +8,7 @@ namespace PlumbingRepairContracts.ViewModels
{
[DisplayName("Номер")]
public int Id { get; set; }
[DisplayName("Номер работы")]
public int WorkId { get; set; }
[DisplayName("Работа")]

View File

@ -0,0 +1,11 @@
namespace PlumbingRepairContracts.ViewModels
{
public class ReportOrderByDateViewModel
{
public DateTime DateCreate { get; set; }
public int Count { get; set; }
public double Sum{ get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace PlumbingRepairContracts.ViewModels
{
public class ReportShopWorkViewModel
{
public string ShopName { get; set; } = string.Empty;
public int TotalCount { get; set; }
public List<(string Work, int Count)> Works { get; set; } = new();
}
}

View File

@ -0,0 +1,22 @@
using PlumbingRepairDataModels.Models;
using System.ComponentModel;
namespace PlumbingRepairContracts.ViewModels
{
public class ShopViewModel
{
public int Id { get; set; }
[DisplayName("Название магазина")]
public string ShopName { get; set; } = string.Empty;
[DisplayName("Адрес")]
public string Address { get; set; } = string.Empty;
[DisplayName("Максимальное количество работ")]
public int maxCountWorks { get; set; }
[DisplayName("Дата открытия")]
public DateTime DateOpening { get; set; }
public Dictionary<int, (IWorkModel, int)> ShopWorks { get; set; } = new();
}
}

View File

@ -0,0 +1,15 @@
namespace PlumbingRepairDataModels.Models
{
public class IShopModel
{
string ShopName { get; }
string Address { get; }
DateTime DateOpening { get; }
int maxCountWorks { get; }
Dictionary<int, (IWorkModel, int)> ShopWorks { get; }
}
}

View File

@ -0,0 +1,143 @@

using Microsoft.EntityFrameworkCore;
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDatabaseImplement.Models;
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairDatabaseImplement.Implements
{
public class ShopStorage : IShopStorage
{
public List<ShopViewModel> GetFullList()
{
using var context = new PlumbingRepairDatabase();
return context.Shops
.Include(x => x.Works)
.ThenInclude(x => x.Work)
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
public List<ShopViewModel> GetFilteredList(ShopSearchModel model)
{
if (string.IsNullOrEmpty(model.ShopName))
{
return new();
}
using var context = new PlumbingRepairDatabase();
return context.Shops
.Include(x => x.Works)
.ThenInclude(x => x.Work)
.Where(x => x.ShopName.Contains(model.ShopName))
.ToList()
.Select(x => x.GetViewModel)
.ToList();
}
public ShopViewModel? GetElement(ShopSearchModel model)
{
if (string.IsNullOrEmpty(model.ShopName) && !model.Id.HasValue)
{
return null;
}
using var context = new PlumbingRepairDatabase();
return context.Shops
.Include(x => x.Works)
.ThenInclude(x => x.Work)
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.ShopName) && x.ShopName == model.ShopName) ||
(model.Id.HasValue && x.Id == model.Id))
?.GetViewModel;
}
public ShopViewModel? Insert(ShopBindingModel model)
{
using var context = new PlumbingRepairDatabase();
var newShop = Shop.Create(context, model);
if (newShop == null)
{
return null;
}
context.Shops.Add(newShop);
context.SaveChanges();
return newShop.GetViewModel;
}
public ShopViewModel? Update(ShopBindingModel model)
{
using var context = new PlumbingRepairDatabase();
using var transaction = context.Database.BeginTransaction();
try
{
var shop = context.Shops.FirstOrDefault(rec => rec.Id == model.Id);
if (shop == null)
{
return null;
}
shop.Update(model);
context.SaveChanges();
shop.UpdateWorks(context, model);
transaction.Commit();
return shop.GetViewModel;
}
catch
{
transaction.Rollback();
throw;
}
}
public ShopViewModel? Delete(ShopBindingModel model)
{
using var context = new PlumbingRepairDatabase();
var element = context.Shops
.Include(x => x.Works)
.FirstOrDefault(rec => rec.Id == model.Id);
if (element != null)
{
context.Shops.Remove(element);
context.SaveChanges();
return element.GetViewModel;
}
return null;
}
public bool SellWork(IWorkModel model, int count)
{
if (model == null)
return false;
using var context = new PlumbingRepairDatabase();
using var transaction = context.Database.BeginTransaction();
foreach (var shopWork in context.ShopWorks.Where(x => x.WorkId == model.Id))
{
int min = Math.Min(shopWork.Count, count);
if (min == shopWork.Count)
{
context.ShopWorks.Remove(shopWork);
}
else
{
shopWork.Count -= min;
}
count -= min;
if (count <= 0)
{
break;
}
}
if (count > 0)
{
transaction.Rollback();
return false;
}
context.SaveChanges();
transaction.Commit();
return true;
}
}
}

View File

@ -12,8 +12,8 @@ using PlumbingRepairDatabaseImplement;
namespace PlumbingRepairDatabaseImplement.Migrations
{
[DbContext(typeof(PlumbingRepairDatabase))]
[Migration("20240405092648_InitialCreate")]
partial class InitialCreate
[Migration("20240501122640_InitCreate")]
partial class InitCreate
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
@ -107,6 +107,59 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.ToTable("Orders");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Shop", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("DateOpening")
.HasColumnType("datetime2");
b.Property<string>("ShopName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("maxCountWorks")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Shops");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.ShopWork", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<int>("Count")
.HasColumnType("int");
b.Property<int>("ShopId")
.HasColumnType("int");
b.Property<int>("WorkId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ShopId");
b.HasIndex("WorkId");
b.ToTable("ShopWorks");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Property<int>("Id")
@ -172,6 +225,25 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.ShopWork", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Shop", "Shop")
.WithMany("Works")
.HasForeignKey("ShopId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PlumbingRepairDatabaseImplement.Models.Work", "Work")
.WithMany()
.HasForeignKey("WorkId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Shop");
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.WorkComponent", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Component", "Component")
@ -201,6 +273,11 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.Navigation("WorkComponents");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Shop", b =>
{
b.Navigation("Works");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Navigation("Components");

View File

@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace PlumbingRepairDatabaseImplement.Migrations
{
public partial class InitialCreate : Migration
public partial class InitCreate : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
@ -38,6 +38,22 @@ namespace PlumbingRepairDatabaseImplement.Migrations
table.PrimaryKey("PK_Components", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Shops",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ShopName = table.Column<string>(type: "nvarchar(max)", nullable: false),
Address = table.Column<string>(type: "nvarchar(max)", nullable: false),
DateOpening = table.Column<DateTime>(type: "datetime2", nullable: false),
maxCountWorks = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Shops", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Works",
columns: table => new
@ -83,6 +99,33 @@ namespace PlumbingRepairDatabaseImplement.Migrations
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "ShopWorks",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
WorkId = table.Column<int>(type: "int", nullable: false),
ShopId = table.Column<int>(type: "int", nullable: false),
Count = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ShopWorks", x => x.Id);
table.ForeignKey(
name: "FK_ShopWorks_Shops_ShopId",
column: x => x.ShopId,
principalTable: "Shops",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ShopWorks_Works_WorkId",
column: x => x.WorkId,
principalTable: "Works",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "WorkComponents",
columns: table => new
@ -120,6 +163,16 @@ namespace PlumbingRepairDatabaseImplement.Migrations
table: "Orders",
column: "WorkId");
migrationBuilder.CreateIndex(
name: "IX_ShopWorks_ShopId",
table: "ShopWorks",
column: "ShopId");
migrationBuilder.CreateIndex(
name: "IX_ShopWorks_WorkId",
table: "ShopWorks",
column: "WorkId");
migrationBuilder.CreateIndex(
name: "IX_WorkComponents_ComponentId",
table: "WorkComponents",
@ -136,12 +189,18 @@ namespace PlumbingRepairDatabaseImplement.Migrations
migrationBuilder.DropTable(
name: "Orders");
migrationBuilder.DropTable(
name: "ShopWorks");
migrationBuilder.DropTable(
name: "WorkComponents");
migrationBuilder.DropTable(
name: "Clients");
migrationBuilder.DropTable(
name: "Shops");
migrationBuilder.DropTable(
name: "Components");

View File

@ -105,6 +105,59 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.ToTable("Orders");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Shop", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateTime>("DateOpening")
.HasColumnType("datetime2");
b.Property<string>("ShopName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("maxCountWorks")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("Shops");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.ShopWork", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
b.Property<int>("Count")
.HasColumnType("int");
b.Property<int>("ShopId")
.HasColumnType("int");
b.Property<int>("WorkId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ShopId");
b.HasIndex("WorkId");
b.ToTable("ShopWorks");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Property<int>("Id")
@ -170,6 +223,25 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.ShopWork", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Shop", "Shop")
.WithMany("Works")
.HasForeignKey("ShopId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("PlumbingRepairDatabaseImplement.Models.Work", "Work")
.WithMany()
.HasForeignKey("WorkId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Shop");
b.Navigation("Work");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.WorkComponent", b =>
{
b.HasOne("PlumbingRepairDatabaseImplement.Models.Component", "Component")
@ -199,6 +271,11 @@ namespace PlumbingRepairDatabaseImplement.Migrations
b.Navigation("WorkComponents");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Shop", b =>
{
b.Navigation("Works");
});
modelBuilder.Entity("PlumbingRepairDatabaseImplement.Models.Work", b =>
{
b.Navigation("Components");

View File

@ -0,0 +1,109 @@

using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PlumbingRepairDatabaseImplement.Models
{
public class Shop
{
public int Id { get; set; }
[Required]
public string ShopName { get; set; } = string.Empty;
[Required]
public string Address { get; set; } = string.Empty;
[Required]
public DateTime DateOpening { get; set; }
[Required]
public int maxCountWorks { get; set; }
private Dictionary<int, (IWorkModel, int)>? _shopWorks = null;
[NotMapped]
public Dictionary<int, (IWorkModel, int)> ShopWorks
{
get
{
if (_shopWorks == null)
{
_shopWorks = Works
.ToDictionary(recSW => recSW.WorkId, recSW => (recSW.Work as IWorkModel, recSW.Count));
}
return _shopWorks;
}
}
[ForeignKey("ShopId")]
public virtual List<ShopWork> Works { get; set; } = new();
public static Shop? Create(PlumbingRepairDatabase context, ShopBindingModel? model)
{
if (model == null)
return null;
return new Shop()
{
Id = model.Id,
maxCountWorks = model.maxCountWorks,
Address = model.Address,
ShopName = model.ShopName,
DateOpening = model.DateOpening,
Works = model.ShopWorks.Select(x => new ShopWork
{
Work = context.Works.First(y => y.Id == x.Key),
Count = x.Value.Item2
}).ToList()
};
}
public void Update(ShopBindingModel? model)
{
if (model == null)
return;
ShopName = model.ShopName;
Address = model.Address;
DateOpening = model.DateOpening;
maxCountWorks = model.maxCountWorks;
}
public ShopViewModel GetViewModel => new()
{
ShopName = ShopName,
Address = Address,
DateOpening = DateOpening,
maxCountWorks = maxCountWorks,
Id = Id,
ShopWorks = ShopWorks
};
public void UpdateWorks(PlumbingRepairDatabase context, ShopBindingModel model)
{
var shopWorks = context.ShopWorks.Where(rec => rec.ShopId == model.Id).ToList();
if (shopWorks != null && shopWorks.Count > 0)
{
context.ShopWorks.RemoveRange(shopWorks.Where(rec => !model.ShopWorks.ContainsKey(rec.WorkId)));
context.SaveChanges();
// обновили количество у существующих записей
foreach (var updateWork in shopWorks)
{
updateWork.Count = model.ShopWorks[updateWork.WorkId].Item2;
model.ShopWorks.Remove(updateWork.WorkId);
}
context.SaveChanges();
}
var shop = context.Shops.First(x => x.Id == Id);
foreach (var pc in model.ShopWorks)
{
context.ShopWorks.Add(new ShopWork
{
Shop = shop,
Work = context.Works.First(x => x.Id == pc.Key),
Count = pc.Value.Item2
});
context.SaveChanges();
}
_shopWorks = null;
}
}
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
namespace PlumbingRepairDatabaseImplement.Models
{
public class ShopWork
{
public int Id { get; set; }
[Required]
public int WorkId { get; set; }
[Required]
public int ShopId { get; set; }
[Required]
public int Count { get; set; }
public virtual Work Work { get; set; } = new();
public virtual Shop Shop { get; set; } = new();
}
}

View File

@ -9,7 +9,7 @@ namespace PlumbingRepairDatabaseImplement
{
if (optionsBuilder.IsConfigured == false)
{
optionsBuilder.UseSqlServer(@"Server=localhost\SQLEXPRESS;Database=PlumbingRepair;Trusted_Connection=True;");
optionsBuilder.UseSqlServer(@"Server=localhost\SQLEXPRESS;Database=PlumbingRepairHard;Trusted_Connection=True;");
}
base.OnConfiguring(optionsBuilder);
}
@ -21,6 +21,10 @@ namespace PlumbingRepairDatabaseImplement
public virtual DbSet<WorkComponent> WorkComponents { set; get; }
public virtual DbSet<Order> Orders { set; get; }
public virtual DbSet<Shop> Shops { set; get; }
public virtual DbSet<ShopWork> ShopWorks { set; get; }
public virtual DbSet<Client> Clients { set; get; }
}
}

View File

@ -12,12 +12,17 @@ namespace PlumbingRepairFileImplement
private readonly string OrderFileName = "Order.xml";
private readonly string WorkFileName = "Work.xml";
private readonly string ShopFileName = "Shop.xml";
private readonly string ClientFileName = "Client.xml";
public List<Component> Components { get; private set; }
public List<Order> Orders { get; private set; }
public List<Work> Works { get; private set; }
public List<Shop> Shops { get; private set; }
public List<Client> Clients { get; private set; }
public static DataFileSingleton GetInstance()
{
@ -33,6 +38,7 @@ namespace PlumbingRepairFileImplement
public void SaveWorks() => SaveData(Works, WorkFileName, "Works", x => x.GetXElement);
public void SaveOrders() => SaveData(Orders, OrderFileName, "Orders", x => x.GetXElement);
public void SaveShops() => SaveData(Shops, ShopFileName, "Shops", x => x.GetXElement);
public void SaveClients() => SaveData(Clients, ClientFileName, "Clients", x => x.GetXElement);
private DataFileSingleton()
@ -40,6 +46,7 @@ namespace PlumbingRepairFileImplement
Components = LoadData(ComponentFileName, "Component", x => Component.Create(x)!)!;
Works = LoadData(WorkFileName, "Work", x => Work.Create(x)!)!;
Orders = LoadData(OrderFileName, "Order", x => Order.Create(x)!)!;
Shops = LoadData(ShopFileName, "Shop", x => Shop.Create(x)!)!;
Clients = LoadData(ClientFileName, "Client", x => Client.Create(x)!)!;
}

View File

@ -0,0 +1,134 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using PlumbingRepairFileImplement.Models;
namespace PlumbingRepairFileImplement.Implements
{
public class ShopStorage : IShopStorage
{
private readonly DataFileSingleton source;
public ShopStorage()
{
source = DataFileSingleton.GetInstance();
}
public List<ShopViewModel> GetFullList()
{
return source.Shops
.Select(x => x.GetViewModel)
.ToList();
}
public List<ShopViewModel> GetFilteredList(ShopSearchModel model)
{
if (string.IsNullOrEmpty(model.ShopName))
{
return new();
}
return source.Shops
.Where(x => x.ShopName.Contains(model.ShopName))
.Select(x => x.GetViewModel)
.ToList();
}
public ShopViewModel? GetElement(ShopSearchModel model)
{
if (string.IsNullOrEmpty(model.ShopName) && !model.Id.HasValue)
{
return null;
}
return source.Shops
.FirstOrDefault(x => (!string.IsNullOrEmpty(model.ShopName) && x.ShopName == model.ShopName) ||
(model.Id.HasValue && x.Id == model.Id))
?.GetViewModel;
}
public ShopViewModel? Insert(ShopBindingModel model)
{
model.Id = source.Shops.Count > 0 ? source.Shops.Max(x => x.Id) + 1 : 1;
var newShop = Shop.Create(model);
if (newShop == null)
{
return null;
}
source.Shops.Add(newShop);
source.SaveShops();
return newShop.GetViewModel;
}
public ShopViewModel? Update(ShopBindingModel model)
{
var component = source.Shops.FirstOrDefault(x => x.Id == model.Id);
if (component == null)
{
return null;
}
component.Update(model);
source.SaveShops();
return component.GetViewModel;
}
public ShopViewModel? Delete(ShopBindingModel model)
{
var element = source.Shops.FirstOrDefault(x => x.Id == model.Id);
if (element != null)
{
source.Shops.Remove(element);
source.SaveShops();
return element.GetViewModel;
}
return null;
}
public bool SellWork(IWorkModel model, int count)
{
var work = source.Works.FirstOrDefault(x => x.Id == model.Id);
int countInShops = source.Shops
.SelectMany(x => x.ShopWorks)
.Sum(y => y.Key == model.Id ? y.Value.Item2 : 0);
if (work == null || countInShops < count)
{
return false;
}
foreach (var shop in source.Shops)
{
var shopWorks = shop.ShopWorks.Where(x => x.Key == model.Id);
if (shopWorks.Any())
{
var shopWork = shopWorks.First();
int min = Math.Min(shopWork.Value.Item2, count);
if (min == shopWork.Value.Item2)
{
shop.ShopWorks.Remove(shopWork.Key);
}
else
{
shop.ShopWorks[shopWork.Key] = (shopWork.Value.Item1, shopWork.Value.Item2 - min);
}
shop.Update(new ShopBindingModel
{
Id = shop.Id,
ShopName = shop.ShopName,
Address = shop.Address,
DateOpening = shop.DateOpening,
ShopWorks = shop.ShopWorks,
maxCountWorks = shop.maxCountWorks
});
count -= min;
if (count <= 0)
{
break;
}
}
}
source.SaveShops();
return true;
}
}
}

View File

@ -0,0 +1,107 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using System.Xml.Linq;
namespace PlumbingRepairFileImplement.Models
{
public class Shop : IShopModel
{
public int Id { get; private set; }
public string ShopName { get; private set; } = string.Empty;
public string Address { get; private set; } = string.Empty;
public DateTime DateOpening { get; private set; }
public Dictionary<int, int> Works { get; private set; } = new();
private Dictionary<int, (IWorkModel, int)>? _shopWorks = null;
public Dictionary<int, (IWorkModel, int)> ShopWorks
{
get
{
if (_shopWorks == null)
{
var source = DataFileSingleton.GetInstance();
_shopWorks = Works.ToDictionary(x => x.Key,
y => ((source.Works.FirstOrDefault(z => z.Id == y.Key) as IWorkModel)!, y.Value));
}
return _shopWorks;
}
}
public int maxCountWorks { get; private set; }
public static Shop? Create(ShopBindingModel model)
{
if (model == null)
{
return null;
}
return new Shop()
{
Id = model.Id,
ShopName = model.ShopName,
Address = model.Address,
DateOpening = model.DateOpening,
Works = model.ShopWorks.ToDictionary(x => x.Key, x => x.Value.Item2),
maxCountWorks= model.maxCountWorks,
};
}
public static Shop? Create(XElement element)
{
if (element == null)
{
return null;
}
return new Shop()
{
Id = Convert.ToInt32(element.Attribute("Id")!.Value),
ShopName = element.Element("ShopName")!.Value,
Address = element.Element("Address")!.Value,
DateOpening = Convert.ToDateTime(element.Element("DateOpening")!.Value),
maxCountWorks = Convert.ToInt32(element.Element("maxCountWorks")!.Value),
Works = element.Element("ShopWorks")!.Elements("ShopWork")
.ToDictionary(x => Convert.ToInt32(x.Element("Key")?.Value), x => Convert.ToInt32(x.Element("Value")?.Value))
};
}
public void Update(ShopBindingModel model)
{
if (model == null)
{
return;
}
ShopName = model.ShopName;
Address = model.Address;
DateOpening = model.DateOpening;
Works = model.ShopWorks.ToDictionary(x => x.Key, x => x.Value.Item2);
maxCountWorks = model.maxCountWorks;
_shopWorks = null;
}
public ShopViewModel GetViewModel => new()
{
Id = Id,
ShopName = ShopName,
Address = Address,
DateOpening = DateOpening,
ShopWorks = ShopWorks,
maxCountWorks = maxCountWorks,
};
public XElement GetXElement => new("Shop",
new XAttribute("Id", Id),
new XElement("ShopName", ShopName),
new XElement("Address", Address),
new XElement("DateOpening", DateOpening.ToString()),
new XElement("maxCountWorks", maxCountWorks.ToString()),
new XElement("ShopWorks",
Works.Select(x => new XElement("ShopWork",
new XElement("Key", x.Key),
new XElement("Value", x.Value))).ToArray()));
}
}

View File

@ -13,11 +13,14 @@ namespace PlumbingRepairListImplement
public List<Work> Works { get; set; }
public List<Client> Clients { get; set; }
public List<Shop> Shops { get; set; }
private DataListSingleton()
{
Components = new List<Component>();
Orders = new List<Order>();
Works = new List<Work>();
Shops = new List<Shop>();
Clients = new List<Client>();
}

View File

@ -0,0 +1,113 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.StoragesContracts;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
using PlumbingRepairListImplement.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PlumbingRepairListImplement.Implements
{
public class ShopStorage : IShopStorage
{
private readonly DataListSingleton _source;
public ShopStorage()
{
_source = DataListSingleton.GetInstance();
}
public List<ShopViewModel> GetFullList()
{
var result = new List<ShopViewModel>();
foreach (var shop in _source.Shops)
{
result.Add(shop.GetViewModel);
}
return result;
}
public List<ShopViewModel> GetFilteredList(ShopSearchModel model)
{
var result = new List<ShopViewModel>();
if (string.IsNullOrEmpty(model.ShopName))
{
return result;
}
foreach (var shop in _source.Shops)
{
if (shop.ShopName.Contains(model.ShopName))
{
result.Add(shop.GetViewModel);
}
}
return result;
}
public ShopViewModel? GetElement(ShopSearchModel model)
{
if (string.IsNullOrEmpty(model.ShopName) && !model.Id.HasValue)
{
return null;
}
foreach (var shop in _source.Shops)
{
if ((!string.IsNullOrEmpty(model.ShopName) && shop.ShopName == model.ShopName) || (model.Id.HasValue && shop.Id == model.Id))
{
return shop.GetViewModel;
}
}
return null;
}
public ShopViewModel? Insert(ShopBindingModel model)
{
model.Id = 1;
foreach (var shop in _source.Shops)
{
if (model.Id <= shop.Id)
{
model.Id = shop.Id + 1;
}
}
var newShop = Shop.Create(model);
if (newShop == null)
{
return null;
}
_source.Shops.Add(newShop);
return newShop.GetViewModel;
}
public ShopViewModel? Update(ShopBindingModel model)
{
foreach (var shop in _source.Shops)
{
if (shop.Id == model.Id)
{
shop.Update(model);
return shop.GetViewModel;
}
}
return null;
}
public ShopViewModel? Delete(ShopBindingModel model)
{
for (int i = 0; i < _source.Shops.Count; ++i)
{
if (_source.Shops[i].Id == model.Id)
{
var element = _source.Shops[i];
_source.Shops.RemoveAt(i);
return element.GetViewModel;
}
}
return null;
}
public bool SellWork(IWorkModel model, int count)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,60 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairListImplement.Models
{
public class Shop
{
public int Id { get; private set; }
public string ShopName { get; private set; } = string.Empty;
public string Address { get; private set; } = string.Empty;
public DateTime DateOpening { get; private set; }
public Dictionary<int, (IWorkModel, int)> ShopWorks { get; private set; } = new Dictionary<int, (IWorkModel, int)>();
public int maxCountWorks { get; private set; }
public static Shop? Create(ShopBindingModel? model)
{
if (model == null)
{
return null;
}
return new Shop()
{
Id = model.Id,
ShopName = model.ShopName,
Address = model.Address,
DateOpening = model.DateOpening,
ShopWorks = model.ShopWorks,
maxCountWorks = model.maxCountWorks
};
}
public void Update(ShopBindingModel? model)
{
if (model == null)
{
return;
}
ShopName = model.ShopName;
Address = model.Address;
DateOpening= model.DateOpening;
ShopWorks= model.ShopWorks;
maxCountWorks= model.maxCountWorks;
}
public ShopViewModel GetViewModel => new()
{
Id = Id,
ShopName = ShopName,
Address = Address,
DateOpening = DateOpening,
ShopWorks = ShopWorks,
maxCountWorks = maxCountWorks
};
}
}

View File

@ -0,0 +1,133 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.BusinessLogicsContracts;
using PlumbingRepairContracts.SearchModels;
using PlumbingRepairContracts.ViewModels;
using Microsoft.AspNetCore.Mvc;
using PlumbingRepairDataModels.Models;
namespace PlumbingRepairRestApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class ShopController : Controller
{
private readonly ILogger _logger;
private readonly IShopLogic _logic;
public ShopController(IShopLogic logic, ILogger<ShopController> logger)
{
_logger = logger;
_logic = logic;
}
[HttpGet]
public List<ShopViewModel>? GetShopList()
{
try
{
return _logic.ReadList(null);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения списка магазинов");
throw;
}
}
[HttpGet]
public Tuple<ShopViewModel, List<WorkViewModel>, List<int>>? GetShopWorks(int shopId)
{
try
{
var shop = _logic.ReadElement(new() { Id = shopId });
if (shop == null)
{
return null;
}
var tuple = Tuple.Create(shop,
shop.ShopWorks.Select(x => new WorkViewModel()
{
Id = x.Value.Item1.Id,
Price = x.Value.Item1.Price,
WorkName = x.Value.Item1.WorkName,
}).ToList(),
shop.ShopWorks.Select(x => x.Value.Item2).ToList());
return tuple;
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения магазина с работами");
throw;
}
}
[HttpGet]
public Dictionary<int, (IWorkModel, int)>? GetListWork(int shopId)
{
try
{
var shop = _logic.ReadElement(new() { Id = shopId });
if (shop == null)
{
return null;
}
return shop.ShopWorks;
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка получения работ магазина");
throw;
}
}
[HttpPost]
public void CreateShop(ShopBindingModel model)
{
try
{
_logic.Create(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка создания магазина");
throw;
}
}
[HttpPost]
public void UpdateShop(ShopBindingModel model)
{
try
{
_logic.Update(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка обновления магазина");
throw;
}
}
[HttpPost]
public void DeleteShop(ShopBindingModel model)
{
try
{
_logic.Delete(model);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка удаления магазина");
throw;
}
}
[HttpPost]
public void StoreReplenishment(Tuple<ShopSearchModel, WorkViewModel, int> shopWorks)
{
try
{
_logic.StoreReplenishment(shopWorks.Item1, shopWorks.Item2, shopWorks.Item3);
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка добавления заготовок в магазин");
throw;
}
}
}
}

View File

@ -13,10 +13,12 @@ builder.Logging.AddLog4Net("log4net.config");
builder.Services.AddTransient<IClientStorage, ClientStorage>();
builder.Services.AddTransient<IOrderStorage, OrderStorage>();
builder.Services.AddTransient<IWorkStorage, WorkStorage>();
builder.Services.AddTransient<IShopStorage, ShopStorage>();
builder.Services.AddTransient<IOrderLogic, OrderLogic>();
builder.Services.AddTransient<IClientLogic, ClientLogic>();
builder.Services.AddTransient<IWorkLogic, WorkLogic>();
builder.Services.AddTransient<IShopLogic, ShopLogic>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle

View File

@ -0,0 +1,58 @@
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;
namespace PlumbingRepairShopApp
{
public class APIClient
{
private static readonly HttpClient _client = new();
public static string Password { get; private set; } = string.Empty;
Review

private

private
public static bool AuthenticationDone { get; private set; }
public static bool Authentication(string password)
{
if (password == Password)
{
AuthenticationDone = true;
}
return AuthenticationDone;
}
public static void Connect(IConfiguration configuration)
{
Password = configuration["Password"];
_client.BaseAddress = new Uri(configuration["IPAddress"]);
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public static T? GetRequest<T>(string requestUrl)
{
var response = _client.GetAsync(requestUrl);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (response.Result.IsSuccessStatusCode)
{
return JsonConvert.DeserializeObject<T>(result);
}
else
{
throw new Exception(result);
}
}
public static void PostRequest<T>(string requestUrl, T model)
{
var json = JsonConvert.SerializeObject(model);
var data = new StringContent(json, Encoding.UTF8, "application/json");
var response = _client.PostAsync(requestUrl, data);
var result = response.Result.Content.ReadAsStringAsync().Result;
if (!response.Result.IsSuccessStatusCode)
{
throw new Exception(result);
}
}
}
}

View File

@ -0,0 +1,208 @@
using PlumbingRepairContracts.BindingModels;
using PlumbingRepairContracts.ViewModels;
using PlumbingRepairClientApp.Models;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using PlumbingRepairDataModels.Models;
using PlumbingRepairContracts.SearchModels;
namespace PlumbingRepairShopApp.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
if (!APIClient.AuthenticationDone)
{
return Redirect("~/Home/Enter");
}
return View(APIClient.GetRequest<List<ShopViewModel>>($"api/shop/getshoplist"));
}
[HttpGet]
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
[HttpGet]
public IActionResult Enter()
{
return View();
}
[HttpPost]
public void Enter(string password)
{
if (string.IsNullOrEmpty(password))
{
throw new Exception("Введите пароль");
}
if (!APIClient.Authentication(password))
{
throw new Exception("Неверный пароль");
}
Response.Redirect("Index");
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
public void Create(string shopName, string address, DateTime dateOpening, int maxCountWorks)
{
if (!APIClient.AuthenticationDone)
{
throw new Exception("Вы как суда попали? Суда вход только авторизованным");
}
if (string.IsNullOrEmpty(shopName))
{
throw new Exception("Название магазина не указано");
}
if (string.IsNullOrEmpty(address))
{
throw new Exception("Адрес магазина не указан");
}
if (maxCountWorks <= 0)
{
throw new Exception("Вместимость магазина должна быть больше нуля");
}
APIClient.PostRequest("api/shop/createshop", new ShopBindingModel
{
ShopName = shopName,
Address = address,
DateOpening = dateOpening,
maxCountWorks = maxCountWorks
});
Response.Redirect("Index");
}
[HttpGet]
public Tuple<ShopViewModel, string>? GetShopWorks(int shopId)
{
var result = APIClient.GetRequest<Tuple<ShopViewModel, List<WorkViewModel>, List<int>>>
($"api/shop/getshopworks?shopId={shopId}");
if (result == null)
{
return default;
}
string manufactureTable = "";
for (int i = 0; i < result.Item2.Count; i++)
{
var manufacture = result.Item2[i];
var count = result.Item3[i];
manufactureTable += "<tr>";
manufactureTable += $"<td>{manufacture.WorkName}</td>";
manufactureTable += $"<td>{manufacture.Price}</td>";
manufactureTable += $"<td>{count}</td>";
manufactureTable += "</tr>";
}
return Tuple.Create(result.Item1, manufactureTable);
}
[HttpGet]
public IActionResult Update()
{
if (!APIClient.AuthenticationDone)
{
return Redirect("~/Home/Enter");
}
ViewBag.Shops = APIClient.GetRequest<List<ShopViewModel>>($"api/shop/getshoplist");
return View();
}
[HttpPost]
public void Update(int shop, string shopName, string address, DateTime dateOpening, int maxCountWorks)
{
if (!APIClient.AuthenticationDone)
{
throw new Exception("Вы как суда попали? Суда вход только авторизованным");
}
if (string.IsNullOrEmpty(shopName))
{
throw new Exception("Название магазина не указано");
}
if (string.IsNullOrEmpty(address))
{
throw new Exception("Адрес магазина не указан");
}
if (maxCountWorks <= 0)
{
throw new Exception("Вместимость магазина должна быть больше нуля");
}
var shopWorks = APIClient.GetRequest<Dictionary<int, (IWorkModel, int)>>($"api/shop/getlistwork?shopId={shop}");
APIClient.PostRequest("api/shop/updateshop", new ShopBindingModel
{
Id = shop,
ShopName = shopName,
Address = address,
DateOpening = dateOpening.Date,
maxCountWorks = maxCountWorks,
ShopWorks = shopWorks
});
Response.Redirect("Index");
}
[HttpGet]
public IActionResult Delete()
{
if (!APIClient.AuthenticationDone)
{
return Redirect("~/Home/Enter");
}
ViewBag.Shops = APIClient.GetRequest<List<ShopViewModel>>($"api/shop/getshoplist");
return View();
}
[HttpPost]
public void Delete(int shop)
{
if (!APIClient.AuthenticationDone)
{
throw new Exception("Вы как суда попали? Суда вход только авторизованным");
}
APIClient.PostRequest("api/shop/deleteshop", new ShopBindingModel
{
Id = shop,
});
Response.Redirect("Index");
}
[HttpGet]
public IActionResult AddWork()
{
ViewBag.Shops = APIClient.GetRequest<List<ShopViewModel>>("api/shop/getshoplist");
ViewBag.Works = APIClient.GetRequest<List<WorkViewModel>>("api/main/getworklist");
return View();
}
[HttpPost]
public void AddWork(int shop, int work, int count)
{
if (!APIClient.AuthenticationDone)
{
throw new Exception("Вы как суда попали? Суда вход только авторизованным");
}
if (count <= 0)
{
throw new Exception("Количество должно быть больше 0");
}
APIClient.PostRequest("api/shop/storereplenishment", Tuple.Create(
new ShopSearchModel() { Id = shop },
new WorkViewModel() { Id = work },
count
));
Response.Redirect("Index");
}
}
}

View File

@ -0,0 +1,9 @@
namespace PlumbingRepairClientApp.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View File

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PlumbingRepairDatabaseImplement\PlumbingRepairDatabaseImplement.csproj" />
<ProjectReference Include="..\PlumbingRepairRestApi\PlumbingRepairRestApi.csproj" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>

View File

@ -0,0 +1,30 @@
using PlumbingRepairShopApp;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
APIClient.Connect(builder.Configuration);
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

View File

@ -0,0 +1,28 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:8533",
"sslPort": 44388
}
},
"profiles": {
"PlumbingRepairShopApp": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7067;http://localhost:5112",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,26 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

View File

@ -0,0 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
namespace PlumbingRepairShopApp.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

View File

@ -0,0 +1,33 @@
@using PlumbingRepairContracts.ViewModels;
@using PlumbingRepairDataModels.Models;
@model Dictionary<int, (IWorkModel, int)>
@{
ViewData["Title"] = "AddWork";
}
<div class="text-center">
<h2 class="display-4">Пополнения магазина работой</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Выбранный магазин:</div>
<div class="col-8">
<select id="shop" name="shop" class="form-control" asp-items="@(new SelectList(@ViewBag.Shops, "Id", "ShopName"))"></select>
</div>
</div>
<div class="row">
<div class="col-4">Выбранная работа:</div>
<div class="col-8">
<select id="work" name="work" class="form-control" asp-items="@(new SelectList(@ViewBag.Works, "Id", "WorkName"))"></select>
</div>
</div>
<div class="row">
<div class="col-4">Количество:</div>
<div class="col-8"><input type="text" id="count" name="count" /></div>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Добавить" class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,28 @@
@{
ViewData["Title"] = "Create";
}
<div class="text-center">
<h2 class="display-4">Создание магазина</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Название магазина:</div>
<div class="col-8"><input type="text" name="shopName" id="shopName" /></div>
</div>
<div class="row">
<div class="col-4">Адрес:</div>
<div class="col-8"><input type="text" name="address" id="address" /></div>
</div>
<div class="row">
<div class="col-4">Дата открытия:</div>
<div class="col-8"><input type="date" id="dateOpening" name="dateOpening" /></div>
</div>
<div class="row">
<div class="col-4">Вместительность:</div>
<div class="col-8"><input type="number" id="maxCountWorks" name="maxCountWorks" /></div>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Создать" class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,18 @@
@{
ViewData["Title"] = "Delete";
}
<div class="text-center">
<h2 class="display-4">Удаление магазина</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Выбранный магазин:</div>
<div class="col-8">
<select id="shop" name="shop" class="form-control" asp-items="@(new SelectList(@ViewBag.Shops, "Id", "ShopName"))"></select>
</div>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Удалить" class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,17 @@
@{
ViewData["Title"] = "Enter";
}
<div class="text-center">
<h2 class="display-4">Вход в приложение</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Пароль:</div>
<div class="col-8"><input type="password" name="password" /></div>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Вход" class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,72 @@
@using PlumbingRepairContracts.ViewModels
@model List<ShopViewModel>
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Магазины</h1>
</div>
<div class="text-center">
@{
if (Model == null)
{
<h3 class="display-4">Авторизируйтесь</h3>
return;
}
<p>
<a asp-action="Create">Создать магазин</a>
<a asp-action="Update">Обновить магазин</a>
<a asp-action="Delete">Удалить магазин</a>
<a asp-action="AddWork">Добавить работу</a>
</p>
<table class="table">
<thead>
<tr>
<th>
Номер
</th>
<th>
Название магазина
</th>
<th>
Адрес
</th>
<th>
Дата открытия
</th>
<th>
Вместительность
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Id)
</td>
<td>
@Html.DisplayFor(modelItem => item.ShopName)
</td>
<td>
@Html.DisplayFor(modelItem => item.Address)
</td>
<td>
@Html.DisplayFor(modelItem => item.DateOpening)
</td>
<td>
@Html.DisplayFor(modelItem => item.maxCountWorks)
</td>
</tr>
}
</tbody>
</table>
}
</div>

View File

@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

View File

@ -0,0 +1,25 @@
@{
ViewData["Title"] = "Register";
}
<div class="text-center">
<h2 class="display-4">Регистрация</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Логин:</div>
<div class="col-8"><input type="text" name="login" /></div>
</div>
<div class="row">
<div class="col-4">Пароль:</div>
<div class="col-8"><input type="password" name="password" /></div>
</div>
<div class="row">
<div class="col-4">ФИО:</div>
<div class="col-8"><input type="text" name="fio" /></div>
</div>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Регистрация" class="btn btn-primary" /></div>
</div>
</form>

View File

@ -0,0 +1,77 @@
@{
ViewData["Title"] = "Update";
}
<div class="text-center">
<h2 class="display-4">Изменение магазина</h2>
</div>
<form method="post">
<div class="row">
<div class="col-4">Магазин:</div>
<div class="col-8">
<select id="shop" name="shop" class="form-control" asp-items="@(new SelectList(@ViewBag.Shops, "Id", "ShopName"))"></select>
</div>
</div>
<div class="row">
<div class="col-4">Название магазина:</div>
<div class="col-8"><input type="text" name="shopName" id="shopName" /></div>
</div>
<div class="row">
<div class="col-4">Адрес:</div>
<div class="col-8"><input type="text" name="address" id="address" /></div>
</div>
<div class="row">
<div class="col-4">Дата открытия:</div>
<div class="col-8"><input type="datetime-local" id="dateOpening" name="dateOpening" /></div>
</div>
<div class="row">
<div class="col-4">Вместительность:</div>
<div class="col-8"><input type="number" id="maxCountWorks" name="maxCountWorks" /></div>
</div>
<table class="table">
<thead>
<tr>
<th>
Название работы
</th>
<th>
Цена
</th>
<th>
Количество
</th>
</tr>
</thead>
<tbody id="works-table">
</tbody>
</table>
<div class="row">
<div class="col-8"></div>
<div class="col-4"><input type="submit" value="Изменить" class="btn btn-primary" /></div>
</div>
</form>
@section Scripts {
<script>
function check() {
var shop = $('#shop').val();
if (shop) {
$.ajax({
method: "GET",
url: "/Home/GetShopWorks",
data: { shopId: shop },
success: function (result) {
if (result != null) {
$('#shopName').val(result.item1.shopName);
$('#address').val(result.item1.address);
$('#dateOpening').val(result.item1.dateOpening);
$('#capacity').val(result.item1.capacity);
$('#works-table').html(result.item2);
}
}
});
};
}
check();
$('#shop').on('change', (e) => check());
</script>
}

View File

@ -0,0 +1,10 @@
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace PlumbingRepairShopApp.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}

View File

@ -0,0 +1,8 @@
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

View File

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace PlumbingRepairShopApp.Pages
{
public class PrivacyModel : PageModel
{
private readonly ILogger<PrivacyModel> _logger;
public PrivacyModel(ILogger<PrivacyModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
}
}

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - PlumbingRepairShopApp</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/PlumbingRepairShopApp.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">PlumbingRepairShopApp</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Магазины</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Enter" asp-action="Enter">Вход</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2023 - PlumbingRepairShopApp - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

View File

@ -0,0 +1,48 @@
/* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
for details on configuring this project to bundle and minify static web assets. */
a.navbar-brand {
white-space: normal;
text-align: center;
word-break: break-all;
}
a {
color: #0077cc;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.border-top {
border-top: 1px solid #e5e5e5;
}
.border-bottom {
border-bottom: 1px solid #e5e5e5;
}
.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
}
button.accept-policy {
font-size: 1rem;
line-height: inherit;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
white-space: nowrap;
line-height: 60px;
}

View File

@ -0,0 +1,2 @@
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

View File

@ -0,0 +1,3 @@
@using PlumbingRepairShopApp
@namespace PlumbingRepairShopApp.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

View File

@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"IPAddress": "http://localhost:5012/",
"Password": "123"
}

View File

@ -0,0 +1,18 @@
html {
font-size: 14px;
}
@media (min-width: 768px) {
html {
font-size: 16px;
}
}
html {
position: relative;
min-height: 100%;
}
body {
margin-bottom: 60px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -0,0 +1,4 @@
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
// for details on configuring this project to bundle and minify static web assets.
// Write your JavaScript code.

View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2011-2021 Twitter, Inc.
Copyright (c) 2011-2021 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,427 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr /* rtl:ignore */;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,424 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr ;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.1.0 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More