20 Commits

Author SHA1 Message Date
maxim
9699999f8a fix problem 2025-09-14 08:44:15 +04:00
maxim
1b6691fa23 тест сломался 2025-09-12 14:52:25 +04:00
maxim
95e254c5c1 бб 2025-09-12 14:18:44 +04:00
maxim
8c31a8d9e2 б 2025-09-12 14:16:41 +04:00
nezui1
beb2573977 соответствуют требованиям 2025-09-12 11:38:17 +04:00
nezui1
551fb7d000 готово 2025-09-10 14:18:04 +04:00
nezui1
77837c6c90 тесты работают 2025-09-10 14:09:38 +04:00
nezui1
0db164d1a9 тесты 2025-09-10 12:24:10 +04:00
nezui1
d88a07037a хранилища верные 2025-09-08 22:27:18 +04:00
nezui1
378aa945dc OfficePackage 2025-09-08 22:17:50 +04:00
nezui1
b6177d2b4d ReportController, ReportAdapter вроде готовы 2025-09-08 22:07:29 +04:00
nezui1
2c33509cb1 подготовка 2025-09-08 20:50:09 +04:00
nezui1
67c5a3b13d начало 2025-09-07 23:40:47 +04:00
nezui1
4c0050e498 лаба готова 2025-09-07 23:27:21 +04:00
nezui1
4f92cf60d6 Должность 2025-09-07 17:55:01 +04:00
nezui1
6da51e4ac6 исправить Salary, доделать тесты. осталось 27 минут видоса 2025-09-03 00:05:52 +04:00
nezui1
31edceff7d тесты для поста работают 2025-09-02 23:57:57 +04:00
nezui1
f9f9219bc5 PostStorageContract переход 2025-09-02 23:24:46 +04:00
nezui1
494f92118a PostDataModelTest исправлены 2025-09-02 22:48:42 +04:00
nezui1
1848377cfd Первая миграция 2025-09-02 22:39:49 +04:00
77 changed files with 5615 additions and 237 deletions

View File

@@ -0,0 +1,311 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketBuisnessLogic.OfficePackage;
using TwoFromTheCasketContratcs.BuisnessLogicsContracts;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.Extensions;
using TwoFromTheCasketContratcs.StorageContracts;
namespace TwoFromTheCasketBuisnessLogic.Implementations;
internal class ReportContract(IServiceStorageContract serviceStorageContract, IMasterStorageContract masterStorageContract, IOrderStorageContract orderStorageContract, ISalaryStorageContract salaryStorageContract, IPostStorageContract postStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, ILogger logger) : IReportContract
{
private readonly IServiceStorageContract _serviceStorageContract = serviceStorageContract;
private readonly IMasterStorageContract _masterStorageContract = masterStorageContract;
private readonly IOrderStorageContract _orderStorageContract = orderStorageContract;
private readonly ISalaryStorageContract _salaryStorageContract = salaryStorageContract;
private readonly IPostStorageContract _postStorageContract = postStorageContract;
private readonly BaseWordBuilder _baseWordBuilder = baseWordBuilder;
private readonly BaseExcelBuilder _baseExcelBuilder = baseExcelBuilder;
private readonly BasePdfBuilder _basePdfBuilder = basePdfBuilder;
private readonly ILogger _logger = logger;
internal static readonly string[] documentHeader = ["Дата", "Работа"];
internal static readonly string[] tableHeader = ["Дата", "Сумма", "Работа", "Объём"];
public async Task<Stream> CreateDocumentServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
var services = await GetDataServicesWithHistoryAsync(dateStart, dateFinish, ct);
var builder = _baseWordBuilder
.AddHeader("Отчет по услугам и их истории изменений")
.AddParagraph($"Период: с {dateStart:dd.MM.yyyy} по {dateFinish:dd.MM.yyyy}")
.AddParagraph($"Отчёт сформирован: {DateTime.Now:dd.MM.yyyy HH:mm}");
if (!services.Any())
{
builder.AddParagraph("За указанный период услуги не найдены.");
return builder.Build();
}
var tableData = new List<string[]>
{
new[] { "Услуга", "Тип", "Текущая цена", "Дата изменения", "Старая цена" }
};
foreach (var service in services)
{
if (service.History.Any())
{
var sortedHistory = service.History.OrderByDescending(h => h.ChangeDate).ToList();
for (int i = 0; i < sortedHistory.Count; i++)
{
var history = sortedHistory[i];
tableData.Add(new[]
{
i == 0 ? service.ServiceName : "",
i == 0 ? service.ServiceType.ToString() : "",
i == 0 ? service.Price.ToString("C") : "",
history.ChangeDate.ToString("dd.MM.yyyy HH:mm"),
history.OldPrice.ToString("C")
});
}
}
else
{
tableData.Add(new[]
{
service.ServiceName,
service.ServiceType.ToString(),
service.Price.ToString("C"),
"—",
"—"
});
}
}
builder.AddTable(new[] { 2500, 1500, 1500, 2000, 1500 }, tableData);
return builder.Build();
}
public async Task<Stream> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
var orders = await GetDataOrderByPeriodAsync(dateStart, dateFinish, ct);
var tableData = new List<string[]> { new[] { "Дата заказа", "Статус", "Тип помещения", "Мастер", "Услуга", "Время работы", "Сумма" } };
foreach (var order in orders)
{
var orderDetails = await GetOrderDetailsAsync(order.Id, ct);
if (orderDetails.Any())
{
foreach (var detail in orderDetails)
{
tableData.Add(new[]
{
order.Date.ToString("dd.MM.yyyy"),
order.Status.ToString(),
order.RoomType.ToString(),
detail.MasterFIO,
detail.ServiceName,
$"{detail.TimeOfWorking} ч.",
detail.TotalAmount.ToString("C")
});
}
}
else
{
tableData.Add(new[]
{
order.Date.ToString("dd.MM.yyyy"),
order.Status.ToString(),
order.RoomType.ToString(),
"Не назначен",
"Нет услуг",
"0 ч.",
"0,00 ₽"
});
}
}
return _baseExcelBuilder
.AddHeader("Отчет по продажам за период", 0, 7)
.AddParagraph($"с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}", 2)
.AddTable([15, 12, 15, 20, 25, 12, 15], tableData)
.Build();
}
public async Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
var data = await GetDataSalaryByPeriodAsync(dateStart, dateFinish, ct);
if (!data.Any())
{
return _basePdfBuilder
.AddHeader("Зарплатная ведомость")
.AddParagraph($"за период с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}")
.AddParagraph("За указанный период зарплаты не найдены.")
.Build();
}
var groupedData = data.GroupBy(x => x.MasterFIO)
.Select(g => new { MasterFIO = g.Key, TotalSalary = g.Sum(x => x.TotalSalary) })
.ToList();
var tableData = new List<string[]>
{
new[] { "ФИО мастера", "Дата зачисления", "Сумма зарплаты" }
};
foreach (var salary in data)
{
var row = new[]
{
salary.MasterFIO,
salary.FromPeriod.ToString("dd.MM.yyyy"),
salary.TotalSalary.ToString("C")
};
tableData.Add(row);
}
return _basePdfBuilder
.AddHeader("Зарплатная ведомость")
.AddParagraph($"за период с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}")
.AddPieChart("Начисления", groupedData.Select(x => (x.MasterFIO, x.TotalSalary)).ToList())
.AddParagraph("")
.AddTable(new[] { 200, 150, 150 }, tableData)
.Build();
}
public async Task<List<ServiceHistoryDataModel>> GetDataServiceHistoryAsync(CancellationToken ct)
{
var histories = new List<ServiceHistoryDataModel>();
var services = _serviceStorageContract.GetList() ?? new List<ServiceDataModel>();
foreach (var service in services)
{
var serviceHistories = _serviceStorageContract.GetHistoryByServiceId(service.Id);
if (serviceHistories != null)
{
histories.AddRange(serviceHistories);
}
}
return histories.OrderByDescending(h => h.ChangeDate).ToList();
}
public async Task<List<OrderDataModel>> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish);
}
try
{
var orders = await _orderStorageContract.GetListAsync(dateStart, dateFinish, ct);
return orders.OrderBy(o => o.Date).ToList();
}
catch (Exception ex)
{
throw;
}
}
public async Task<List<MasterSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart > dateFinish)
{
throw new IncorrectDatesException(dateStart, dateFinish);
}
var salaries = await _salaryStorageContract.GetListAsync(dateStart, dateFinish, ct);
var masters = _masterStorageContract.GetList(onlyActive: true) ?? new List<MasterDataModel>();
var salaryRecords = salaries
.GroupBy(salary => salary.MasterId)
.Select(group =>
{
var master = masters.FirstOrDefault(m => m.Id == group.Key);
var totalSalary = group.Sum(salary => salary.Salary + salary.Prize);
return new MasterSalaryByPeriodDataModel
{
MasterFIO = master?.FIO ?? "Неизвестный мастер",
TotalSalary = totalSalary,
FromPeriod = dateStart,
ToPeriod = dateFinish
};
})
.OrderBy(x => x.MasterFIO)
.ToList();
return salaryRecords;
}
public async Task<List<ServiceWithHistoryDataModel>> GetDataServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish);
}
var services = _serviceStorageContract.GetList() ?? new List<ServiceDataModel>();
var result = new List<ServiceWithHistoryDataModel>();
foreach (var service in services)
{
var serviceHistories = _serviceStorageContract.GetHistoryByServiceId(service.Id) ?? new List<ServiceHistoryDataModel>();
var filteredHistories = serviceHistories
.Where(h => h.ChangeDate >= dateStart && h.ChangeDate <= dateFinish)
.OrderByDescending(h => h.ChangeDate)
.ToList();
result.Add(new ServiceWithHistoryDataModel
{
Id = service.Id,
ServiceName = service.ServiceName,
ServiceType = service.ServiceType,
MasterId = service.MasterId,
Price = service.Price,
IsDeleted = service.IsDeleted,
History = filteredHistories
});
}
return result.OrderBy(s => s.ServiceName).ToList();
}
private async Task<List<(string MasterFIO, string ServiceName, int TimeOfWorking, decimal TotalAmount)>> GetOrderDetailsAsync(string orderId, CancellationToken ct)
{
try
{
return await _orderStorageContract.GetOrderDetailsAsync(orderId, ct);
}
catch (Exception)
{
return new List<(string MasterFIO, string ServiceName, int TimeOfWorking, decimal TotalAmount)>();
}
}
}

View File

@@ -1,25 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using TwoFromTheCasketContratcs.BuisnessLogicsContracts;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.StorageContracts;
using TwoFromTheCasketContratcs.Extensions;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.Infrastructure;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketBuisnessLogic.Implementations
{
internal class SalaryBusinessLogicContract(ISalaryStorageContract
salaryStorageContract, IPostStorageContract
postStorageContract, IMasterStorageContract masterStorageContract, ILogger
logger, IOrderStorageContract orderStorageContract) : ISalaryBuisnessLogicContract
logger, IOrderStorageContract orderStorageContract, IConfigurationSalary configurationSalary) : ISalaryBuisnessLogicContract
{
private readonly ILogger _logger = logger;
private readonly ISalaryStorageContract _salaryStorageContract = salaryStorageContract;
private readonly IPostStorageContract _postStorageContract = postStorageContract;
private readonly IMasterStorageContract _masterStorageContract = masterStorageContract;
private readonly IOrderStorageContract _orderStorageContract = orderStorageContract;
private readonly IConfigurationSalary _configurationSalary = configurationSalary;
private readonly object _lockObject = new object();
public List<SalaryDataModel> GetAllSalariesByPeriod(DateTime fromDate, DateTime toDate)
{
@@ -57,27 +62,152 @@ logger, IOrderStorageContract orderStorageContract) : ISalaryBuisnessLogicContra
public void CalculateSalaryByMonth(DateTime date)
{
_logger.LogInformation("CalculateSalaryByMonth: {date}", date);
var startDate = new DateTime(date.Year, date.Month, 1);
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
var startDate = new DateTime(date.Year, date.Month, 1, 0, 0, 0, DateTimeKind.Utc);
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), 0, 0, 0, DateTimeKind.Utc);
var masters = _masterStorageContract.GetList() ?? throw new NullListException();
foreach (var master in masters)
{
master.Validate();
var orders = _orderStorageContract.GetList() ?? throw new NullListException();
// Фильтруем заказы по дате (связь с мастером идет через ServiceOrder)
var masterOrders = orders.Where(o => o.Date >= startDate && o.Date <= finishDate).ToList();
var post = _postStorageContract.GetElementById(master.PostId) ?? throw new ElementNotFoundException(master.PostId);
var baseSalary = post.Salary;
var prize = 500.0;
var totalSalary = baseSalary + prize;
_logger.LogDebug("The master {master.Id} was paid a salary of {salary}", master.Id, totalSalary);
_salaryStorageContract.AddElement(new SalaryDataModel(master.Id, finishDate, totalSalary, prize));
var salary = post.ConfigurationModel switch
{
null => 0,
CarpenterPostConfiguration cpc => CalculateSalaryForCarpenter(masterOrders, startDate, finishDate, cpc),
PainterPostConfiguration ppc => CalculateSalaryForPainter(masterOrders, startDate, finishDate, ppc),
PlastererPostConfiguration plpc => CalculateSalaryForPlasterer(masterOrders, startDate, finishDate, plpc),
PostConfiguration pc => pc.Rate,
};
_logger.LogDebug("The master {masterId} was paid a salary of {salary}", master.Id, salary);
_salaryStorageContract.AddElement(new SalaryDataModel(master.Id, finishDate, salary, 0));
}
// Сохраняем все изменения одним вызовом
_salaryStorageContract.SaveChanges();
}
private double CalculateSalaryForCarpenter(List<OrderDataModel> orders, DateTime startDate, DateTime finishDate, CarpenterPostConfiguration config)
{
var maxThreads = Math.Max(1, _configurationSalary.MaxThreads);
var options = new ParallelOptions { MaxDegreeOfParallelism = maxThreads };
var calcPercent = 0.0;
var days = new List<DateTime>();
for (var date = startDate; date < finishDate; date = date.AddDays(1))
{
days.Add(date);
}
Parallel.ForEach(days, options, date =>
{
var ordersInDay = orders.Where(x => x.Date.Date == date.Date).ToArray();
if (ordersInDay.Length > 0)
{
lock (_lockObject)
{
calcPercent += ordersInDay.Length * config.BonusForExtraCarpentry;
}
}
});
return config.Rate + calcPercent;
}
private double CalculateSalaryForPainter(List<OrderDataModel> orders, DateTime startDate, DateTime finishDate, PainterPostConfiguration config)
{
var maxThreads = Math.Max(1, _configurationSalary.MaxThreads);
var options = new ParallelOptions { MaxDegreeOfParallelism = maxThreads };
var calcPercent = 0.0;
var days = new List<DateTime>();
for (var date = startDate; date < finishDate; date = date.AddDays(1))
{
days.Add(date);
}
Parallel.ForEach(days, options, date =>
{
var ordersInDay = orders.Where(x => x.Date.Date == date.Date).ToArray();
if (ordersInDay.Length > 0)
{
lock (_lockObject)
{
calcPercent += ordersInDay.Length * config.PainterPercent;
}
}
});
var calcBonusTask = Task.Run(() =>
{
return orders.Count * config.BonusForExtraPainter;
});
try
{
calcBonusTask.Wait();
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
{
_logger.LogError(ex, "Error in the painter payroll process");
}
return 0;
}
return config.Rate + calcPercent + calcBonusTask.Result;
}
private double CalculateSalaryForPlasterer(List<OrderDataModel> orders, DateTime startDate, DateTime finishDate, PlastererPostConfiguration config)
{
var maxThreads = Math.Max(1, _configurationSalary.MaxThreads);
var options = new ParallelOptions { MaxDegreeOfParallelism = maxThreads };
var calcPercent = 0.0;
var days = new List<DateTime>();
for (var date = startDate; date < finishDate; date = date.AddDays(1))
{
days.Add(date);
}
Parallel.ForEach(days, options, date =>
{
var ordersInDay = orders.Where(x => x.Date.Date == date.Date).ToArray();
if (ordersInDay.Length > 0)
{
lock (_lockObject)
{
calcPercent += ordersInDay.Length * config.PlastererPercent;
}
}
});
var calcBonusTask = Task.Run(() =>
{
return orders.Count * config.BonusForExtraPlasterer;
});
try
{
calcBonusTask.Wait();
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
{
_logger.LogError(ex, "Error in the plasterer payroll process");
}
return 0;
}
return config.Rate + calcPercent + calcBonusTask.Result;
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
public abstract class BaseExcelBuilder
{
public abstract BaseExcelBuilder AddHeader(string header, int startIndex, int count);
public abstract BaseExcelBuilder AddParagraph(string text, int columnIndex);
public abstract BaseExcelBuilder AddTable(int[] columnsWidths, List<string[]> data);
public abstract Stream Build();
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
public abstract class BasePdfBuilder
{
public abstract BasePdfBuilder AddHeader(string header);
public abstract BasePdfBuilder AddParagraph(string text);
public abstract BasePdfBuilder AddPieChart(string title, List<(string Caption, double Value)> data);
public abstract BasePdfBuilder AddTable(int[] widths, List<string[]> data);
public abstract Stream Build();
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
public abstract class BaseWordBuilder
{
public abstract BaseWordBuilder AddHeader(string header);
public abstract BaseWordBuilder AddParagraph(string text);
public abstract BaseWordBuilder AddTable(int[] widths, List<string[]> data);
public abstract Stream Build();
}

View File

@@ -0,0 +1,115 @@
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
using MigraDoc.Rendering;
using System.Threading.Tasks;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
internal class MigraDocPdfBuilder : BasePdfBuilder
{
private readonly Document _document;
public MigraDocPdfBuilder()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
_document = new Document();
DefineStyles();
}
public override BasePdfBuilder AddHeader(string header)
{
_document.AddSection().AddParagraph(header, "NormalBold");
return this;
}
public override BasePdfBuilder AddParagraph(string text)
{
_document.LastSection.AddParagraph(text, "Normal");
return this;
}
public override BasePdfBuilder AddPieChart(string title, List<(string Caption, double Value)> data)
{
if (data == null || data.Count == 0)
{
return this;
}
var chart = new Chart(ChartType.Pie2D);
var series = chart.SeriesCollection.AddSeries();
series.Add(data.Select(x => x.Value).ToArray());
var xseries = chart.XValues.AddXSeries();
xseries.Add(data.Select(x => x.Caption).ToArray());
chart.DataLabel.Type = DataLabelType.Percent;
chart.DataLabel.Position = DataLabelPosition.OutsideEnd;
chart.Width = Unit.FromCentimeter(16);
chart.Height = Unit.FromCentimeter(12);
chart.TopArea.AddParagraph(title);
chart.XAxis.MajorTickMark = TickMarkType.Outside;
chart.YAxis.MajorTickMark = TickMarkType.Outside;
chart.YAxis.HasMajorGridlines = true;
chart.PlotArea.LineFormat.Width = 1;
chart.PlotArea.LineFormat.Visible = true;
chart.TopArea.AddLegend();
_document.LastSection.Add(chart);
return this;
}
public override BasePdfBuilder AddTable(int[] widths, List<string[]> data)
{
if (data == null || data.Count == 0)
{
return this;
}
var table = _document.LastSection.AddTable();
table.Style = "Table";
table.Borders.Color = Colors.Black;
table.Borders.Width = 0.25;
table.Borders.Left.Width = 0.5;
table.Borders.Right.Width = 0.5;
table.Rows.LeftIndent = 0;
// Настройка колонок
for (int i = 0; i < widths.Length; i++)
{
var column = table.AddColumn(Unit.FromPoint(widths[i]));
column.Format.Alignment = ParagraphAlignment.Center;
}
// Добавление данных
for (int rowIndex = 0; rowIndex < data.Count; rowIndex++)
{
var row = table.AddRow();
row.HeadingFormat = (rowIndex == 0); // Первая строка - заголовок
row.Format.Alignment = ParagraphAlignment.Center;
row.Format.Font.Bold = (rowIndex == 0);
for (int colIndex = 0; colIndex < data[rowIndex].Length && colIndex < widths.Length; colIndex++)
{
var cell = row.Cells[colIndex];
var cellText = data[rowIndex][colIndex];
cell.AddParagraph(cellText);
cell.Format.Alignment = ParagraphAlignment.Center;
}
}
return this;
}
public override Stream Build()
{
var stream = new MemoryStream();
var renderer = new PdfDocumentRenderer(true)
{
Document = _document
};
renderer.RenderDocument();
renderer.PdfDocument.Save(stream);
return stream;
}
private void DefineStyles()
{
var style = _document.Styles.AddStyle("NormalBold", "Normal");
style.Font.Bold = true;
}
}

View File

@@ -0,0 +1,271 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
internal class OpenXmlExcelBuilder : BaseExcelBuilder
{
private readonly SheetData _sheetData;
private readonly MergeCells _mergeCells;
private readonly Columns _columns;
private uint _rowIndex = 0;
public OpenXmlExcelBuilder()
{
_sheetData = new SheetData();
_mergeCells = new MergeCells();
_columns = new Columns();
_rowIndex = 1;
}
public override BaseExcelBuilder AddHeader(string header, int startIndex, int count)
{
CreateCell(startIndex, _rowIndex, header, StyleIndex.BoldTextWithoutBorder);
for (int i = startIndex + 1; i < startIndex + count; ++i)
{
CreateCell(i, _rowIndex, "", StyleIndex.SimpleTextWithoutBorder);
}
_mergeCells.Append(new MergeCell()
{
Reference =
new StringValue($"{GetExcelColumnName(startIndex)}{_rowIndex}:{GetExcelColumnName(startIndex + count - 1)}{_rowIndex}")
});
_rowIndex++;
return this;
}
public override BaseExcelBuilder AddParagraph(string text, int columnIndex)
{
CreateCell(columnIndex, _rowIndex++, text, StyleIndex.SimpleTextWithoutBorder);
return this;
}
public override BaseExcelBuilder AddTable(int[] columnsWidths, List<string[]> data)
{
if (columnsWidths == null || columnsWidths.Length == 0)
{
throw new ArgumentNullException(nameof(columnsWidths));
}
if (data == null || data.Count == 0)
{
throw new ArgumentNullException(nameof(data));
}
if (data.Any(x => x.Length != columnsWidths.Length))
{
throw new InvalidOperationException("widths.Length != data.Length");
}
uint counter = 1;
int coef = 2;
_columns.Append(columnsWidths.Select(x => new Column
{
Min = counter,
Max = counter++,
Width = x * coef,
CustomWidth = true
}));
for (var j = 0; j < data.First().Length; ++j)
{
CreateCell(j, _rowIndex, data.First()[j], StyleIndex.BoldTextWithBorder);
}
_rowIndex++;
for (var i = 1; i < data.Count - 1; ++i)
{
for (var j = 0; j < data[i].Length; ++j)
{
CreateCell(j, _rowIndex, data[i][j], StyleIndex.SimpleTextWithBorder);
}
_rowIndex++;
}
for (var j = 0; j < data.Last().Length; ++j)
{
CreateCell(j, _rowIndex, data.Last()[j], StyleIndex.BoldTextWithBorder);
}
_rowIndex++;
return this;
}
public override Stream Build()
{
var stream = new MemoryStream();
using var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook);
var workbookpart = spreadsheetDocument.AddWorkbookPart();
GenerateStyle(workbookpart);
workbookpart.Workbook = new Workbook();
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
if (_columns.HasChildren)
{
worksheetPart.Worksheet.Append(_columns);
}
worksheetPart.Worksheet.Append(_sheetData);
var sheets = spreadsheetDocument.WorkbookPart!.Workbook.AppendChild(new Sheets());
var sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Лист 1"
};
sheets.Append(sheet);
if (_mergeCells.HasChildren)
{
worksheetPart.Worksheet.InsertAfter(_mergeCells, worksheetPart.Worksheet.Elements<SheetData>().First());
}
return stream;
}
private static void GenerateStyle(WorkbookPart workbookPart)
{
var workbookStylesPart = workbookPart.AddNewPart<WorkbookStylesPart>();
workbookStylesPart.Stylesheet = new Stylesheet();
var fonts = new Fonts() { Count = 2, KnownFonts = BooleanValue.FromBoolean(true) };
fonts.Append(new DocumentFormat.OpenXml.Spreadsheet.Font
{
FontSize = new FontSize() { Val = 11 },
FontName = new FontName() { Val = "Calibri" },
FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
FontScheme = new FontScheme() { Val = new EnumValue<FontSchemeValues>(FontSchemeValues.Minor) }
});
fonts.Append(new DocumentFormat.OpenXml.Spreadsheet.Font
{
FontSize = new FontSize() { Val = 11 },
FontName = new FontName() { Val = "Calibri" },
FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
FontScheme = new FontScheme() { Val = new EnumValue<FontSchemeValues>(FontSchemeValues.Minor) },
Bold = new Bold()
});
workbookStylesPart.Stylesheet.Append(fonts);
// Default Fill
var fills = new Fills() { Count = 1 };
fills.Append(new Fill
{
PatternFill = new PatternFill() { PatternType = new EnumValue<PatternValues>(PatternValues.None) }
});
workbookStylesPart.Stylesheet.Append(fills);
// Default Border
var borders = new Borders() { Count = 2 };
borders.Append(new Border
{
LeftBorder = new LeftBorder(),
RightBorder = new RightBorder(),
TopBorder = new TopBorder(),
BottomBorder = new BottomBorder(),
DiagonalBorder = new DiagonalBorder()
});
borders.Append(new Border
{
LeftBorder = new LeftBorder() { Style = BorderStyleValues.Thin },
RightBorder = new RightBorder() { Style = BorderStyleValues.Thin },
TopBorder = new TopBorder() { Style = BorderStyleValues.Thin },
BottomBorder = new BottomBorder() { Style = BorderStyleValues.Thin }
});
workbookStylesPart.Stylesheet.Append(borders);
// Default cell format and a date cell format
var cellFormats = new CellFormats() { Count = 4 };
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 0,
BorderId = 0,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Left,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 0,
BorderId = 1,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Left,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 1,
BorderId = 0,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Center,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 1,
BorderId = 1,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Center,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
workbookStylesPart.Stylesheet.Append(cellFormats);
}
private enum StyleIndex
{
SimpleTextWithoutBorder = 0,
SimpleTextWithBorder = 1,
BoldTextWithoutBorder = 2,
BoldTextWithBorder = 3
}
private void CreateCell(int columnIndex, uint rowIndex, string text, StyleIndex styleIndex)
{
var columnName = GetExcelColumnName(columnIndex);
var cellReference = columnName + rowIndex;
var row = _sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex! == rowIndex);
if (row == null)
{
row = new Row() { RowIndex = rowIndex };
_sheetData.Append(row);
}
var newCell = row.Elements<Cell>()
.FirstOrDefault(c => c.CellReference != null && c.CellReference.Value == columnName + rowIndex);
if (newCell == null)
{
Cell? refCell = null;
foreach (Cell cell in row.Elements<Cell>())
{
if (cell.CellReference?.Value != null && cell.CellReference.Value.Length == cellReference.Length)
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
}
newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
}
newCell.CellValue = new CellValue(text);
newCell.DataType = CellValues.String;
newCell.StyleIndex = (uint)styleIndex;
}
private static string GetExcelColumnName(int columnNumber)
{
columnNumber += 1;
int dividend = columnNumber;
string columnName = string.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (dividend - modulo) / 26;
}
return columnName;
}
}

View File

@@ -0,0 +1,79 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace TwoFromTheCasketBuisnessLogic.OfficePackage;
internal class OpenXmlWordBuilder : BaseWordBuilder
{
private readonly Document _document;
private readonly Body _body;
public OpenXmlWordBuilder()
{
_document = new Document();
_body = _document.AppendChild(new Body());
}
public override BaseWordBuilder AddHeader(string header)
{
var paragraph = _body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new RunProperties(new Bold()));
run.AppendChild(new Text(header));
return this;
}
public override BaseWordBuilder AddParagraph(string text)
{
var paragraph = _body.AppendChild(new Paragraph());
var run = paragraph.AppendChild(new Run());
run.AppendChild(new Text(text));
return this;
}
public override BaseWordBuilder AddTable(int[] widths, List<string[]> data)
{
if (widths == null || widths.Length == 0)
{
throw new ArgumentNullException(nameof(widths));
}
if (data == null || data.Count == 0)
{
throw new ArgumentNullException(nameof(data));
}
if (data.Any(x => x.Length != widths.Length))
{
throw new InvalidOperationException("widths.Length != data.Length");
}
var table = new Table();
table.AppendChild(new TableProperties(
new TableBorders(
new TopBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
new BottomBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
new LeftBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
new RightBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
new InsideHorizontalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 },
new InsideVerticalBorder() { Val = new EnumValue<BorderValues>(BorderValues.Single), Size = 12 }
)
));
// Заголовок
var tr = new TableRow();
for (var j = 0; j < widths.Length; ++j)
{
tr.Append(new TableCell(
new TableCellProperties(new TableCellWidth() { Width = widths[j].ToString() }),
new Paragraph(new Run(new RunProperties(new Bold()), new Text(data.First()[j])))));
}
table.Append(tr);
// Данные
table.Append(data.Skip(1).Select(x =>
new TableRow(x.Select(y => new TableCell(new Paragraph(new Run(new Text(y))))))));
_body.Append(table);
return this;
}
public override Stream Build()
{
var stream = new MemoryStream();
using var wordDocument = WordprocessingDocument.Create(stream, WordprocessingDocumentType.Document);
var mainPart = wordDocument.AddMainDocumentPart();
mainPart.Document = _document;
return stream;
}
}

View File

@@ -12,8 +12,11 @@
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageReference Include="PDFsharp-MigraDoc-gdi" Version="6.2.1" />
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.AdapterContracts.OperationResponses;
namespace TwoFromTheCasketContratcs.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.Infrastructure;
using TwoFromTheCasketContratcs.ViewModels;
namespace TwoFromTheCasketContratcs.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ServiceHistoryViewModel> data) => OK<ReportOperationResponse, List<ServiceHistoryViewModel>>(data);
public static ReportOperationResponse OK(List<OrderViewModel> data) => OK<ReportOperationResponse, List<OrderViewModel>>(data);
public static ReportOperationResponse OK(List<MasterSalaryByPeriodViewModel> data) => OK<ReportOperationResponse, List<MasterSalaryByPeriodViewModel>>(data);
public static ReportOperationResponse OK(List<ServiceWithHistoryViewModel> data) => OK<ReportOperationResponse, List<ServiceWithHistoryViewModel>>(data);
public static ReportOperationResponse OK(Stream data, string fileName) => OK<ReportOperationResponse, Stream>(data, fileName);
public static ReportOperationResponse BadRequest(string message) =>
BadRequest<ReportOperationResponse>(message);
public static ReportOperationResponse InternalServerError(string message) =>
InternalServerError<ReportOperationResponse>(message);
}

View File

@@ -16,5 +16,5 @@ public class PostBindingModel
public string? PostType { get; set; }
public double Salary { get; set; }
public string? ConfigurationJson { get; set; }
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.DataModels;
namespace TwoFromTheCasketContratcs.BuisnessLogicsContracts;
public interface IReportContract
{
Task<List<ServiceHistoryDataModel>> GetDataServiceHistoryAsync(CancellationToken ct);
Task<List<OrderDataModel>> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<MasterSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<ServiceWithHistoryDataModel>> GetDataServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.DataModels;
public class MasterSalaryByPeriodDataModel
{
public required string MasterFIO { get; set; }
public double TotalSalary { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -1,4 +1,7 @@
using System;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -8,11 +11,12 @@ using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.Extensions;
using TwoFromTheCasketContratcs.Infrastructure;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketContratcs.DataModels;
public class PostDataModel(string postId, string postName, PostType
postType, double salary) : IValidation
postType, PostConfiguration configuration) : IValidation
{
public string Id { get; private set; } = postId;
@@ -20,11 +24,27 @@ postType, double salary) : IValidation
public PostType PostType { get; private set; } = postType;
public double Salary { get; private set; } = salary;
public PostConfiguration ConfigurationModel { get; private set; } = configuration;
public PostDataModel(string postId, string postName, PostType postType, string configurationJson) : this(postId,
postName, postType, new PostConfiguration())
{
var obj = JToken.Parse(configurationJson);
if (obj is not null)
{
ConfigurationModel = obj.Value<string>("Type") switch
{
nameof(CarpenterPostConfiguration) => JsonConvert.DeserializeObject<CarpenterPostConfiguration>(
configurationJson)!,
nameof(PainterPostConfiguration) => JsonConvert
.DeserializeObject<PainterPostConfiguration>(configurationJson)!,
nameof(PlastererPostConfiguration) => JsonConvert.DeserializeObject<PlastererPostConfiguration>(
configurationJson)!,
_ => JsonConvert.DeserializeObject<PostConfiguration>(configurationJson)!,
};
}
}
public void Validate()
{
if (Id.IsEmpty())
@@ -40,7 +60,10 @@ postType, double salary) : IValidation
if (PostType == PostType.None)
throw new ValidationException("Field PostType is empty");
if (Salary <= 0)
throw new ValidationException("Field Salary is empty");
if (ConfigurationModel is null)
throw new ValidationException("Field ConfigurationModel is not initialized");
if (ConfigurationModel!.Rate <=0 )
throw new ValidationException("Field Rate is less or equal zero");
}
}

View File

@@ -0,0 +1,15 @@
using TwoFromTheCasketContratcs.Enums;
namespace TwoFromTheCasketContratcs.DataModels;
public class ServiceWithHistoryDataModel
{
public string Id { get; set; } = string.Empty;
public string ServiceName { get; set; } = string.Empty;
public ServiceType ServiceType { get; set; }
public double Price { get; set; }
public string MasterId { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public bool IsDeleted { get; set; }
public List<ServiceHistoryDataModel> History { get; set; } = new();
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.Infrastructure;
public interface IConfigurationSalary
{
double ExtraSaleSum { get; }
double ExtraInstallationSum { get; }
int MaxThreads { get; }
}

View File

@@ -15,6 +15,8 @@ public class OperationResponse
protected object? Result { get; set; }
protected string? FileName { get; set; }
public IActionResult GetResponse(HttpRequest request, HttpResponse response)
{
ArgumentNullException.ThrowIfNull(request);
@@ -32,6 +34,8 @@ public class OperationResponse
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.OK, Result = data };
protected static TResult OK<TResult, TData>(TData data, string fileName) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.OK, Result = data, FileName = fileName };
protected static TResult NoContent<TResult>()
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.NoContent };

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
public class CarpenterPostConfiguration : PostConfiguration
{
public override string Type => nameof(CarpenterPostConfiguration);
public double BonusForExtraCarpentry { get; set; }
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
public class PainterPostConfiguration : PostConfiguration
{
public override string Type => nameof(PainterPostConfiguration);
public double PainterPercent { get; set; }
public double BonusForExtraPainter { get; set; }
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
public class PlastererPostConfiguration : PostConfiguration
{
public override string Type => nameof(PlastererPostConfiguration);
public double PlastererPercent { get; set; }
public double BonusForExtraPlasterer { get; set; }
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
public class PostConfiguration
{
public virtual string Type => nameof(PostConfiguration);
public double Rate { get; set; }
}

View File

@@ -12,6 +12,8 @@ public interface IOrderStorageContract
{
List<OrderDataModel> GetList();
Task<List<OrderDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
OrderDataModel? GetElementById(string id);
OrderDataModel? GetElementByDate(DateTime date);
@@ -23,4 +25,6 @@ public interface IOrderStorageContract
void UpdElement(OrderDataModel orderDataModel);
void DelElement(string id);
Task<List<(string MasterFIO, string ServiceName, int TimeOfWorking, decimal TotalAmount)>> GetOrderDetailsAsync(string orderId, CancellationToken ct);
}

View File

@@ -13,11 +13,13 @@ public interface ISalaryStorageContract
List<SalaryDataModel> GetList(DateTime startDate, DateTime endDate, string?
masterId = null);
Task<List<SalaryDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
SalaryDataModel? GetElementByMasterId(string masterId);
SalaryDataModel? GetElementBySalaryDate(DateTime salaryDate);
void AddElement(SalaryDataModel salaryDataModel);
void SaveChanges();
}

View File

@@ -11,7 +11,8 @@ namespace TwoFromTheCasketContratcs.StorageContracts;
public interface IServiceStorageContract
{
List<ServiceDataModel> GetList();
Task<List<ServiceDataModel>> GetListAsync();
Task<List<ServiceDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
List<ServiceHistoryDataModel> GetHistoryByServiceId(string serviceId);
ServiceDataModel? GetElementById(string id);

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.ViewModels;
public class MasterSalaryByPeriodViewModel
{
public required string MasterFIO { get; set; }
public double TotalSalary { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -14,5 +14,5 @@ public class PostViewModel
public required string PostType { get; set; }
public double Salary { get; set; }
public required string Configuration { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketContratcs.ViewModels;
public class ServiceHistoryViewModel
{
public int Id { get; set; }
public string ServiceId { get; set; } = string.Empty;
public double OldPrice { get; set; }
public DateTime ChangeDate { get; set; }
}

View File

@@ -0,0 +1,15 @@
using TwoFromTheCasketContratcs.Enums;
namespace TwoFromTheCasketContratcs.ViewModels;
public class ServiceWithHistoryViewModel
{
public string Id { get; set; } = string.Empty;
public string ServiceName { get; set; } = string.Empty;
public ServiceType ServiceType { get; set; }
public double Price { get; set; }
public string MasterId { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; }
public bool IsDeleted { get; set; }
public List<ServiceHistoryViewModel> History { get; set; } = new();
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.Infrastructure;
namespace TwoFromTheCasketDatabase;
public class DefaultConfigurationDatabase : IConfigurationDatabase
{
public string ConnectionString => "";
}

View File

@@ -153,5 +153,50 @@ internal class OrderStorageContract : IOrderStorageContract
}
private Order? GetOrderById(string id) => _dbContext.Orders.FirstOrDefault(x => x.Id == id);
public async Task<List<(string MasterFIO, string ServiceName, int TimeOfWorking, decimal TotalAmount)>> GetOrderDetailsAsync(string orderId, CancellationToken ct)
{
try
{
var orderDetails = await _dbContext.ServiceOrders
.Where(so => so.OrderId == orderId)
.Join(_dbContext.Masters, so => so.MasterId, m => m.Id, (so, m) => new { so, m })
.Join(_dbContext.Services, x => x.so.ServiceId, s => s.Id, (x, s) => new
{
MasterFIO = x.m.FIO,
ServiceName = s.ServiceName,
TimeOfWorking = x.so.TimeOfWorking,
ServicePrice = s.Price
})
.ToListAsync(ct);
return orderDetails.Select(x => (
x.MasterFIO,
x.ServiceName,
x.TimeOfWorking,
(decimal)(x.ServicePrice * x.TimeOfWorking) // Простой расчет: цена услуги * время работы
)).ToList();
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
public async Task<List<OrderDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
return [.. await _dbContext.Orders
.Where(x => x.Date >= startDate && x.Date <= endDate)
.Select(x => _mapper.Map<OrderDataModel>(x))
.ToListAsync(ct)];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
}

View File

@@ -29,7 +29,8 @@ internal class PostStorageContract : IPostStorageContract
.ForMember(x => x.Id, x => x.Ignore())
.ForMember(x => x.PostId, x => x.MapFrom(src => src.Id))
.ForMember(x => x.IsActual, x => x.MapFrom(src => true))
.ForMember(x => x.ChangeDate, x => x.MapFrom(src => DateTime.UtcNow));
.ForMember(x => x.ChangeDate, x => x.MapFrom(src => DateTime.UtcNow))
.ForMember(x => x.Configuration, x => x.MapFrom(src => src.ConfigurationModel));
});
_mapper = new Mapper(config);
}

View File

@@ -33,6 +33,7 @@ internal class SalaryStorageContract : ISalaryStorageContract
src.Prize));
cfg.CreateMap<SalaryDataModel, Salary>()
.ForMember(dest => dest.Id, opt => opt.Ignore()) // Игнорируем Id, так как он генерируется автоматически
.ForMember(dest => dest.MasterId, opt => opt.MapFrom(src => src.MasterId))
.ForMember(dest => dest.SalaryDate, opt => opt.MapFrom(src => src.SalaryDate))
.ForMember(dest => dest.SalarySize, opt => opt.MapFrom(src => src.Salary))
@@ -88,6 +89,18 @@ internal class SalaryStorageContract : ISalaryStorageContract
try
{
_dbContext.Salaries.Add(_mapper.Map<Salary>(salaryDataModel));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
public void SaveChanges()
{
try
{
_dbContext.SaveChanges();
}
catch (Exception ex)
@@ -95,6 +108,22 @@ internal class SalaryStorageContract : ISalaryStorageContract
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
public async Task<List<SalaryDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
return [.. await _dbContext.Salaries
.Include(x => x.Master)
.Where(x => x.SalaryDate >= startDate && x.SalaryDate <= endDate)
.Select(x => _mapper.Map<SalaryDataModel>(x))
.ToListAsync(ct)];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
}

View File

@@ -203,4 +203,38 @@ internal class ServiceStorageContract : IServiceStorageContract
}
}
private Service? GetServiceById(string id) => _dbContext.Services.FirstOrDefault(x => x.Id == id && !x.IsDeleted);
public async Task<List<ServiceDataModel>> GetListAsync()
{
try
{
return [.. await _dbContext.Services
.Include(x => x.ServiceHistory)
.Where(x => !x.IsDeleted)
.Select(x => _mapper.Map<ServiceDataModel>(x))
.ToListAsync()];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
public async Task<List<ServiceDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
return [.. await _dbContext.Services
.Include(x => x.ServiceHistory)
.Where(x => !x.IsDeleted)
.Select(x => _mapper.Map<ServiceDataModel>(x))
.ToListAsync(ct)];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex);
}
}
}

View File

@@ -0,0 +1,219 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class FirstMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Masters",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
FIO = table.Column<string>(type: "text", nullable: false),
PostId = table.Column<string>(type: "text", nullable: false),
BirthDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
EmploymentDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Masters", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Orders",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
Date = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
Status = table.Column<int>(type: "integer", nullable: false),
RoomType = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Orders", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Posts",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
PostId = table.Column<string>(type: "text", nullable: false),
PostName = table.Column<string>(type: "text", nullable: false),
PostType = table.Column<int>(type: "integer", nullable: false),
Salary = table.Column<double>(type: "double precision", nullable: false),
IsActual = table.Column<bool>(type: "boolean", nullable: false),
ChangeDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Posts", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Salaries",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
MasterId = table.Column<string>(type: "text", nullable: false),
SalaryDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
SalarySize = table.Column<double>(type: "double precision", nullable: false),
Prize = table.Column<double>(type: "double precision", nullable: false),
SalaryId = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Salaries", x => x.Id);
table.ForeignKey(
name: "FK_Salaries_Masters_SalaryId",
column: x => x.SalaryId,
principalTable: "Masters",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ServiceOrders",
columns: table => new
{
OrderId = table.Column<string>(type: "text", nullable: false),
ServiceId = table.Column<string>(type: "text", nullable: false),
MasterId = table.Column<string>(type: "text", nullable: false),
TimeOfWorking = table.Column<int>(type: "integer", nullable: false),
ServiceOrderId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceOrders", x => new { x.ServiceId, x.MasterId, x.OrderId });
table.ForeignKey(
name: "FK_ServiceOrders_Masters_ServiceOrderId",
column: x => x.ServiceOrderId,
principalTable: "Masters",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Services",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
ServiceName = table.Column<string>(type: "text", nullable: false),
ServiceType = table.Column<int>(type: "integer", nullable: false),
MasterId = table.Column<string>(type: "text", nullable: false),
Price = table.Column<double>(type: "double precision", nullable: false),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
ServiceId = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Services", x => x.Id);
table.ForeignKey(
name: "FK_Services_Masters_ServiceId",
column: x => x.ServiceId,
principalTable: "Masters",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ServiceHistories",
columns: table => new
{
ServiceId = table.Column<string>(type: "text", nullable: false),
OldPrice = table.Column<double>(type: "double precision", nullable: false),
ChangeDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ServiceHistories", x => x.ServiceId);
table.ForeignKey(
name: "FK_ServiceHistories_Services_ServiceId",
column: x => x.ServiceId,
principalTable: "Services",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Masters_Id_IsDeleted",
table: "Masters",
columns: new[] { "Id", "IsDeleted" },
unique: true,
filter: "\"IsDeleted\" = FALSE");
migrationBuilder.CreateIndex(
name: "IX_Orders_Id",
table: "Orders",
column: "Id",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_Posts_PostId_IsActual",
table: "Posts",
columns: new[] { "PostId", "IsActual" },
unique: true,
filter: "\"IsActual\" = TRUE");
migrationBuilder.CreateIndex(
name: "IX_Posts_PostName_IsActual",
table: "Posts",
columns: new[] { "PostName", "IsActual" },
unique: true,
filter: "\"IsActual\" = TRUE");
migrationBuilder.CreateIndex(
name: "IX_Salaries_SalaryId",
table: "Salaries",
column: "SalaryId");
migrationBuilder.CreateIndex(
name: "IX_ServiceOrders_ServiceOrderId",
table: "ServiceOrders",
column: "ServiceOrderId");
migrationBuilder.CreateIndex(
name: "IX_Services_ServiceId",
table: "Services",
column: "ServiceId");
migrationBuilder.CreateIndex(
name: "IX_Services_ServiceName_IsDeleted",
table: "Services",
columns: new[] { "ServiceName", "IsDeleted" },
unique: true,
filter: "\"IsDeleted\" = FALSE");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Orders");
migrationBuilder.DropTable(
name: "Posts");
migrationBuilder.DropTable(
name: "Salaries");
migrationBuilder.DropTable(
name: "ServiceHistories");
migrationBuilder.DropTable(
name: "ServiceOrders");
migrationBuilder.DropTable(
name: "Services");
migrationBuilder.DropTable(
name: "Masters");
}
}
}

View File

@@ -0,0 +1,277 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
[Migration("20250907135104_ChangeFieldsInPost")]
partial class ChangeFieldsInPost
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("SalaryId");
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.HasKey("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("MasterId")
.HasColumnType("text");
b.Property<string>("OrderId")
.HasColumnType("text");
b.Property<string>("ServiceOrderId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceId", "MasterId", "OrderId");
b.HasIndex("ServiceOrderId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany("Salaries")
.HasForeignKey("SalaryId");
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class ChangeFieldsInPost : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Salary",
table: "Posts");
migrationBuilder.AddColumn<string>(
name: "Configuration",
table: "Posts",
type: "jsonb",
nullable: false,
defaultValue: "{\"Rate\" : 0, \"Type\" : \"PostConfiguration\"}");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Configuration",
table: "Posts");
migrationBuilder.AddColumn<double>(
name: "Salary",
table: "Posts",
type: "double precision",
nullable: false,
defaultValue: 0.0);
}
}
}

View File

@@ -0,0 +1,280 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
[Migration("20250907172931_AddDateOfDeleteToMasterAndIdToSalary")]
partial class AddDateOfDeleteToMasterAndIdToSalary
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DateOfDelete")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("SalaryId");
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.HasKey("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("MasterId")
.HasColumnType("text");
b.Property<string>("OrderId")
.HasColumnType("text");
b.Property<string>("ServiceOrderId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceId", "MasterId", "OrderId");
b.HasIndex("ServiceOrderId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany("Salaries")
.HasForeignKey("SalaryId");
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,59 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class AddDateOfDeleteToMasterAndIdToSalary : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Salaries_Masters_MasterId",
table: "Salaries");
migrationBuilder.DropIndex(
name: "IX_Salaries_MasterId",
table: "Salaries");
migrationBuilder.CreateIndex(
name: "IX_Salaries_SalaryId",
table: "Salaries",
column: "SalaryId");
migrationBuilder.AddForeignKey(
name: "FK_Salaries_Masters_SalaryId",
table: "Salaries",
column: "SalaryId",
principalTable: "Masters",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Salaries_Masters_SalaryId",
table: "Salaries");
migrationBuilder.DropIndex(
name: "IX_Salaries_SalaryId",
table: "Salaries");
migrationBuilder.CreateIndex(
name: "IX_Salaries_MasterId",
table: "Salaries",
column: "MasterId");
migrationBuilder.AddForeignKey(
name: "FK_Salaries_Masters_MasterId",
table: "Salaries",
column: "MasterId",
principalTable: "Masters",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@@ -0,0 +1,285 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
[Migration("20250907182212_AddSalaryForeignKey")]
partial class AddSalaryForeignKey
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("MasterId");
b.HasIndex("SalaryId");
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.HasKey("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("MasterId")
.HasColumnType("text");
b.Property<string>("OrderId")
.HasColumnType("text");
b.Property<string>("ServiceOrderId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceId", "MasterId", "OrderId");
b.HasIndex("ServiceOrderId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany()
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Salaries")
.HasForeignKey("SalaryId");
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class AddSalaryForeignKey : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateIndex(
name: "IX_Salaries_MasterId",
table: "Salaries",
column: "MasterId");
migrationBuilder.AddForeignKey(
name: "FK_Salaries_Masters_MasterId",
table: "Salaries",
column: "MasterId",
principalTable: "Masters",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Salaries_Masters_MasterId",
table: "Salaries");
migrationBuilder.DropIndex(
name: "IX_Salaries_MasterId",
table: "Salaries");
}
}
}

View File

@@ -0,0 +1,280 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
[Migration("20250907183927_FixSalaryForeignKeyConfiguration")]
partial class FixSalaryForeignKeyConfiguration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("MasterId", "SalaryDate")
.IsUnique();
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.HasKey("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("MasterId")
.HasColumnType("text");
b.Property<string>("OrderId")
.HasColumnType("text");
b.Property<string>("ServiceOrderId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceId", "MasterId", "OrderId");
b.HasIndex("ServiceOrderId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany("Salaries")
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,57 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class FixSalaryForeignKeyConfiguration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Salaries_Masters_SalaryId",
table: "Salaries");
migrationBuilder.DropIndex(
name: "IX_Salaries_MasterId",
table: "Salaries");
migrationBuilder.DropIndex(
name: "IX_Salaries_SalaryId",
table: "Salaries");
migrationBuilder.CreateIndex(
name: "IX_Salaries_MasterId_SalaryDate",
table: "Salaries",
columns: new[] { "MasterId", "SalaryDate" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_Salaries_MasterId_SalaryDate",
table: "Salaries");
migrationBuilder.CreateIndex(
name: "IX_Salaries_MasterId",
table: "Salaries",
column: "MasterId");
migrationBuilder.CreateIndex(
name: "IX_Salaries_SalaryId",
table: "Salaries",
column: "SalaryId");
migrationBuilder.AddForeignKey(
name: "FK_Salaries_Masters_SalaryId",
table: "Salaries",
column: "SalaryId",
principalTable: "Masters",
principalColumn: "Id");
}
}
}

View File

@@ -0,0 +1,313 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
[Migration("20250912071637_FixServiceOrderStructure")]
partial class FixServiceOrderStructure
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("MasterId", "SalaryDate")
.IsUnique();
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceOrderId")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OrderId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ServiceId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceOrderId");
b.HasIndex("MasterId");
b.HasIndex("OrderId");
b.HasIndex("ServiceId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany("Salaries")
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany()
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Order", null)
.WithMany()
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Service", null)
.WithMany()
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,148 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
/// <inheritdoc />
public partial class FixServiceOrderStructure : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropPrimaryKey(
name: "PK_ServiceOrders",
table: "ServiceOrders");
migrationBuilder.DropIndex(
name: "IX_ServiceOrders_ServiceOrderId",
table: "ServiceOrders");
migrationBuilder.DropPrimaryKey(
name: "PK_ServiceHistories",
table: "ServiceHistories");
migrationBuilder.AddColumn<int>(
name: "Id",
table: "ServiceHistories",
type: "integer",
nullable: false,
defaultValue: 0)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
migrationBuilder.AddPrimaryKey(
name: "PK_ServiceOrders",
table: "ServiceOrders",
column: "ServiceOrderId");
migrationBuilder.AddPrimaryKey(
name: "PK_ServiceHistories",
table: "ServiceHistories",
column: "Id");
migrationBuilder.CreateIndex(
name: "IX_ServiceOrders_MasterId",
table: "ServiceOrders",
column: "MasterId");
migrationBuilder.CreateIndex(
name: "IX_ServiceOrders_OrderId",
table: "ServiceOrders",
column: "OrderId");
migrationBuilder.CreateIndex(
name: "IX_ServiceOrders_ServiceId",
table: "ServiceOrders",
column: "ServiceId");
migrationBuilder.CreateIndex(
name: "IX_ServiceHistories_ServiceId",
table: "ServiceHistories",
column: "ServiceId");
migrationBuilder.AddForeignKey(
name: "FK_ServiceOrders_Masters_MasterId",
table: "ServiceOrders",
column: "MasterId",
principalTable: "Masters",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_ServiceOrders_Orders_OrderId",
table: "ServiceOrders",
column: "OrderId",
principalTable: "Orders",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
migrationBuilder.AddForeignKey(
name: "FK_ServiceOrders_Services_ServiceId",
table: "ServiceOrders",
column: "ServiceId",
principalTable: "Services",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_ServiceOrders_Masters_MasterId",
table: "ServiceOrders");
migrationBuilder.DropForeignKey(
name: "FK_ServiceOrders_Orders_OrderId",
table: "ServiceOrders");
migrationBuilder.DropForeignKey(
name: "FK_ServiceOrders_Services_ServiceId",
table: "ServiceOrders");
migrationBuilder.DropPrimaryKey(
name: "PK_ServiceOrders",
table: "ServiceOrders");
migrationBuilder.DropIndex(
name: "IX_ServiceOrders_MasterId",
table: "ServiceOrders");
migrationBuilder.DropIndex(
name: "IX_ServiceOrders_OrderId",
table: "ServiceOrders");
migrationBuilder.DropIndex(
name: "IX_ServiceOrders_ServiceId",
table: "ServiceOrders");
migrationBuilder.DropPrimaryKey(
name: "PK_ServiceHistories",
table: "ServiceHistories");
migrationBuilder.DropIndex(
name: "IX_ServiceHistories_ServiceId",
table: "ServiceHistories");
migrationBuilder.DropColumn(
name: "Id",
table: "ServiceHistories");
migrationBuilder.AddPrimaryKey(
name: "PK_ServiceOrders",
table: "ServiceOrders",
columns: new[] { "ServiceId", "MasterId", "OrderId" });
migrationBuilder.AddPrimaryKey(
name: "PK_ServiceHistories",
table: "ServiceHistories",
column: "ServiceId");
migrationBuilder.CreateIndex(
name: "IX_ServiceOrders_ServiceOrderId",
table: "ServiceOrders",
column: "ServiceOrderId");
}
}
}

View File

@@ -0,0 +1,310 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using TwoFromTheCasketDatabase;
#nullable disable
namespace TwoFromTheCasketDatabase.Migrations
{
[DbContext(typeof(TwoFromTheCasketDbContext))]
partial class TwoFromTheCasketDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("BirthDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("EmploymentDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("FIO")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("Id", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Masters");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Order", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("Date")
.HasColumnType("timestamp with time zone");
b.Property<int>("RoomType")
.HasColumnType("integer");
b.Property<int>("Status")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Id")
.IsUnique();
b.ToTable("Orders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Post", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Configuration")
.IsRequired()
.HasColumnType("jsonb");
b.Property<bool>("IsActual")
.HasColumnType("boolean");
b.Property<string>("PostId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PostName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("PostType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("PostId", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.HasIndex("PostName", "IsActual")
.IsUnique()
.HasFilter("\"IsActual\" = TRUE");
b.ToTable("Posts");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Prize")
.HasColumnType("double precision");
b.Property<DateTime>("SalaryDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("SalaryId")
.HasColumnType("text");
b.Property<double>("SalarySize")
.HasColumnType("double precision");
b.HasKey("Id");
b.HasIndex("MasterId", "SalaryDate")
.IsUnique();
b.ToTable("Salaries");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<bool>("IsDeleted")
.HasColumnType("boolean");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<double>("Price")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.HasColumnType("text");
b.Property<string>("ServiceName")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ServiceType")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.HasIndex("ServiceName", "IsDeleted")
.IsUnique()
.HasFilter("\"IsDeleted\" = FALSE");
b.ToTable("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTime>("ChangeDate")
.HasColumnType("timestamp with time zone");
b.Property<double>("OldPrice")
.HasColumnType("double precision");
b.Property<string>("ServiceId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("ServiceId");
b.ToTable("ServiceHistories");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.Property<string>("ServiceOrderId")
.HasColumnType("text");
b.Property<string>("MasterId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OrderId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ServiceId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("TimeOfWorking")
.HasColumnType("integer");
b.HasKey("ServiceOrderId");
b.HasIndex("MasterId");
b.HasIndex("OrderId");
b.HasIndex("ServiceId");
b.ToTable("ServiceOrders");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Salary", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", "Master")
.WithMany("Salaries")
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("Master");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("Services")
.HasForeignKey("ServiceId");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceHistory", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Service", "Service")
.WithMany("ServiceHistory")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Service");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.ServiceOrder", b =>
{
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany()
.HasForeignKey("MasterId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Order", null)
.WithMany()
.HasForeignKey("OrderId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Service", null)
.WithMany()
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("TwoFromTheCasketDatabase.Models.Master", null)
.WithMany("ServiceOrders")
.HasForeignKey("ServiceOrderId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Master", b =>
{
b.Navigation("Salaries");
b.Navigation("ServiceOrders");
b.Navigation("Services");
});
modelBuilder.Entity("TwoFromTheCasketDatabase.Models.Service", b =>
{
b.Navigation("ServiceHistory");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -30,7 +30,6 @@ internal class Master
[ForeignKey("SalaryId")]
public List<Salary>? Salaries { get; set; }
[ForeignKey("ServiceOrderId")]
public List<ServiceOrder>? ServiceOrders { get; set; }
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketDatabase.Models;
@@ -18,7 +19,7 @@ internal class Post
public PostType PostType { get; set; }
public double Salary { get; set; }
public required PostConfiguration Configuration { get; set; }
public bool IsActual { get; set; }

View File

@@ -9,6 +9,8 @@ namespace TwoFromTheCasketDatabase.Models;
internal class ServiceHistory
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public required string ServiceId { get; set; }
public required double OldPrice { get; set; }

View File

@@ -9,6 +9,7 @@ namespace TwoFromTheCasketDatabase.Models;
internal class ServiceOrder
{
public string ServiceOrderId { get; set; } = Guid.NewGuid().ToString();
public required string OrderId { get; set; }

View File

@@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore.Design;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TwoFromTheCasketDatabase;
internal class SampleContextFactory : IDesignTimeDbContextFactory<TwoFromTheCasketDbContext>
{
public TwoFromTheCasketDbContext CreateDbContext(string[] args)
{
return new TwoFromTheCasketDbContext(new DefaultConfigurationDatabase());
}
}

View File

@@ -8,6 +8,11 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>

View File

@@ -5,7 +5,10 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using TwoFromTheCasketContratcs.Infrastructure;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketDatabase.Models;
namespace TwoFromTheCasketDatabase;
@@ -38,16 +41,53 @@ internal class TwoFromTheCasketDbContext(IConfigurationDatabase configurationDat
.IsUnique()
.HasFilter($"\"{nameof(Post.IsActual)}\" = TRUE");
modelBuilder.Entity<ServiceHistory>().HasKey(x => x.ServiceId);
modelBuilder.Entity<ServiceHistory>().HasKey(x => x.Id);
modelBuilder.Entity<Service>().HasIndex(e => new { e.ServiceName, e.IsDeleted }).IsUnique().HasFilter($"\"{nameof(Service.IsDeleted)}\" = FALSE");
modelBuilder.Entity<ServiceOrder>().HasKey(x => new { x.ServiceId, x.MasterId, x.OrderId });
modelBuilder.Entity<ServiceOrder>().HasKey(x => x.ServiceOrderId);
// Настройка внешних ключей для ServiceOrder
modelBuilder.Entity<ServiceOrder>()
.HasOne<Order>()
.WithMany()
.HasForeignKey(so => so.OrderId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ServiceOrder>()
.HasOne<Service>()
.WithMany()
.HasForeignKey(so => so.ServiceId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ServiceOrder>()
.HasOne<Master>()
.WithMany(m => m.ServiceOrders)
.HasForeignKey(so => so.MasterId)
.OnDelete(DeleteBehavior.Restrict);
// Настройка внешнего ключа для Salary
modelBuilder.Entity<Salary>()
.HasOne(s => s.Master)
.WithMany(m => m.Salaries)
.HasForeignKey(s => s.MasterId)
.OnDelete(DeleteBehavior.Restrict);
// Настройка уникального индекса для Salary
modelBuilder.Entity<Salary>()
.HasIndex(s => new { s.MasterId, s.SalaryDate })
.IsUnique();
modelBuilder.Entity<Post>().Property(x => x.Configuration)
.HasColumnType("jsonb")
.HasConversion(
x => SerializePostConfiguration(x),
x => DeserializePostConfiguration(x)
);
}
public DbSet<Master> Masters { get; set; }
@@ -62,4 +102,36 @@ internal class TwoFromTheCasketDbContext(IConfigurationDatabase configurationDat
public DbSet<ServiceHistory> ServiceHistories { get; set; }
public DbSet<ServiceOrder> ServiceOrders { get; set; }
private static string SerializePostConfiguration(PostConfiguration postConfiguration) =>
JsonConvert.SerializeObject(postConfiguration, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None,
Formatting = Formatting.None
});
private static PostConfiguration DeserializePostConfiguration(string jsonString)
{
try
{
var token = JToken.Parse(jsonString);
var typeValue = token.Value<string>("Type");
return typeValue switch
{
nameof(CarpenterPostConfiguration) =>
JsonConvert.DeserializeObject<CarpenterPostConfiguration>(jsonString)!,
nameof(PainterPostConfiguration) =>
JsonConvert.DeserializeObject<PainterPostConfiguration>(jsonString)!,
nameof(PlastererPostConfiguration) =>
JsonConvert.DeserializeObject<PlastererPostConfiguration>(jsonString)!,
_ => JsonConvert.DeserializeObject<PostConfiguration>(jsonString)!,
};
}
catch
{
// Fallback to basic PostConfiguration if deserialization fails
return new PostConfiguration { Rate = 0 };
}
}
}

View File

@@ -9,6 +9,7 @@ using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.StorageContracts;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketTest.BuisnessLogicContractTests;
@@ -37,9 +38,9 @@ internal class PostBusinessLogicContractTests
// Arrange
var listOriginal = new List<PostDataModel>()
{
new(Guid.NewGuid().ToString(), "name 1", PostType.Carpenter, 10),
new(Guid.NewGuid().ToString(), "name 2", PostType.Carpenter, 10),
new(Guid.NewGuid().ToString(), "name 3", PostType.Carpenter, 10),
new(Guid.NewGuid().ToString(), "name 1", PostType.Carpenter, new PostConfiguration {Rate = 10}),
new(Guid.NewGuid().ToString(), "name 2", PostType.Carpenter, new PostConfiguration {Rate = 10}),
new(Guid.NewGuid().ToString(), "name 3", PostType.Carpenter, new PostConfiguration { Rate = 10 }),
};
_postStorageContract.Setup(x => x.GetList()).Returns(listOriginal);
@@ -100,8 +101,8 @@ internal class PostBusinessLogicContractTests
var postId = Guid.NewGuid().ToString();
var listOriginal = new List<PostDataModel>()
{
new(postId, "name 1", PostType.Painter, 10),
new(postId, "name 2", PostType.Painter, 10)
new(postId, "name 1", PostType.Painter, new PostConfiguration { Rate = 10 }),
new(postId, "name 2", PostType.Painter, new PostConfiguration { Rate = 10 })
};
_postStorageContract.Setup(x => x.GetPostWithHistory(It.IsAny<string>())).Returns(listOriginal);
@@ -170,7 +171,7 @@ internal class PostBusinessLogicContractTests
{
// Arrange
var id = Guid.NewGuid().ToString();
var record = new PostDataModel(id, "name", PostType.Painter, 10);
var record = new PostDataModel(id, "name", PostType.Painter, new PostConfiguration { Rate = 10 });
_postStorageContract.Setup(x => x.GetElementById(id)).Returns(record);
// Act
@@ -187,7 +188,7 @@ internal class PostBusinessLogicContractTests
{
// Arrange
var postName = "name";
var record = new PostDataModel(Guid.NewGuid().ToString(), postName, PostType.Plasterer, 10);
var record = new PostDataModel(Guid.NewGuid().ToString(), postName, PostType.Plasterer, new PostConfiguration { Rate = 10 });
_postStorageContract.Setup(x => x.GetElementByName(postName)).Returns(record);
// Act
@@ -246,11 +247,11 @@ internal class PostBusinessLogicContractTests
{
// Arrange
var flag = false;
var record = new PostDataModel(Guid.NewGuid().ToString(), "name 1", PostType.Plasterer, 10);
var record = new PostDataModel(Guid.NewGuid().ToString(), "name 1", PostType.Plasterer, new PostConfiguration { Rate = 10 });
_postStorageContract.Setup(x => x.AddElement(It.IsAny<PostDataModel>()))
.Callback((PostDataModel x) =>
{
flag = x.Id == record.Id && x.PostName == record.PostName && x.PostType == record.PostType && x.Salary == record.Salary;
flag = x.Id == record.Id && x.PostName == record.PostName && x.PostType == record.PostType && x.ConfigurationModel.Rate == record.ConfigurationModel.Rate;
});
// Act
@@ -268,7 +269,7 @@ internal class PostBusinessLogicContractTests
_postStorageContract.Setup(x => x.AddElement(It.IsAny<PostDataModel>())).Throws(new ElementExistsException("Data", "Data"));
// Act & Assert
Assert.That(() => _postBusinessLogicContract.InsertPost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10)), Throws.TypeOf<ElementExistsException>());
Assert.That(() => _postBusinessLogicContract.InsertPost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<ElementExistsException>());
_postStorageContract.Verify(x => x.AddElement(It.IsAny<PostDataModel>()), Times.Once);
}
@@ -284,7 +285,7 @@ internal class PostBusinessLogicContractTests
public void InsertPost_InvalidRecord_ThrowException_Test()
{
// Act & Assert
Assert.That(() => _postBusinessLogicContract.InsertPost(new PostDataModel("id", "postName", PostType.Plasterer, 10)), Throws.TypeOf<ValidationException>());
Assert.That(() => _postBusinessLogicContract.InsertPost(new PostDataModel("id", "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<ValidationException>());
_postStorageContract.Verify(x => x.AddElement(It.IsAny<PostDataModel>()), Times.Never);
}
@@ -295,7 +296,7 @@ internal class PostBusinessLogicContractTests
_postStorageContract.Setup(x => x.AddElement(It.IsAny<PostDataModel>())).Throws(new StorageException(new InvalidOperationException()));
// Act & Assert
Assert.That(() => _postBusinessLogicContract.InsertPost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10)), Throws.TypeOf<StorageException>());
Assert.That(() => _postBusinessLogicContract.InsertPost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<StorageException>());
_postStorageContract.Verify(x => x.AddElement(It.IsAny<PostDataModel>()), Times.Once);
}
@@ -304,11 +305,11 @@ internal class PostBusinessLogicContractTests
{
// Arrange
var flag = false;
var record = new PostDataModel(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10);
var record = new PostDataModel(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 });
_postStorageContract.Setup(x => x.UpdElement(It.IsAny<PostDataModel>()))
.Callback((PostDataModel x) =>
{
flag = x.Id == record.Id && x.PostName == record.PostName && x.PostType == record.PostType && x.Salary == record.Salary;
flag = x.Id == record.Id && x.PostName == record.PostName && x.PostType == record.PostType && x.ConfigurationModel.Rate == record.ConfigurationModel.Rate;
});
// Act
@@ -326,7 +327,7 @@ internal class PostBusinessLogicContractTests
_postStorageContract.Setup(x => x.UpdElement(It.IsAny<PostDataModel>())).Throws(new ElementNotFoundException(""));
// Act & Assert
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10)), Throws.TypeOf<ElementNotFoundException>());
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<ElementNotFoundException>());
_postStorageContract.Verify(x => x.UpdElement(It.IsAny<PostDataModel>()), Times.Once);
}
@@ -337,7 +338,7 @@ internal class PostBusinessLogicContractTests
_postStorageContract.Setup(x => x.UpdElement(It.IsAny<PostDataModel>())).Throws(new ElementExistsException("Data", "Data"));
// Act & Assert
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10)), Throws.TypeOf<ElementExistsException>());
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<ElementExistsException>());
_postStorageContract.Verify(x => x.UpdElement(It.IsAny<PostDataModel>()), Times.Once);
}
@@ -353,7 +354,7 @@ internal class PostBusinessLogicContractTests
public void UpdatePost_InvalidRecord_ThrowException_Test()
{
// Act & Assert
Assert.That(() => _postBusinessLogicContract.UpdatePost(new PostDataModel("id", "postName", PostType.Plasterer, 10)), Throws.TypeOf<ValidationException>());
Assert.That(() => _postBusinessLogicContract.UpdatePost(new PostDataModel("id", "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<ValidationException>());
_postStorageContract.Verify(x => x.UpdElement(It.IsAny<PostDataModel>()), Times.Never);
}
@@ -364,7 +365,7 @@ internal class PostBusinessLogicContractTests
_postStorageContract.Setup(x => x.UpdElement(It.IsAny<PostDataModel>())).Throws(new StorageException(new InvalidOperationException()));
// Act & Assert
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, 10)), Throws.TypeOf<StorageException>());
Assert.That(() => _postBusinessLogicContract.UpdatePost(new(Guid.NewGuid().ToString(), "postName", PostType.Plasterer, new PostConfiguration { Rate = 10 })), Throws.TypeOf<StorageException>());
_postStorageContract.Verify(x => x.UpdElement(It.IsAny<PostDataModel>()), Times.Once);
}

View File

@@ -0,0 +1,239 @@
using TwoFromTheCasketBuisnessLogic.Implementations;
using TwoFromTheCasketBuisnessLogic.OfficePackage;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.StorageContracts;
using Microsoft.Extensions.Logging;
using Moq;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketTest.BuisnessLogicContractTests;
[TestFixture]
internal class ReportContractTests
{
private ReportContract _reportContract;
private Mock<IServiceStorageContract> _serviceStorageContract;
private Mock<IMasterStorageContract> _masterStorageContract;
private Mock<IOrderStorageContract> _orderStorageContract;
private Mock<ISalaryStorageContract> _salaryStorageContract;
private Mock<IPostStorageContract> _postStorageContract;
private Mock<BaseWordBuilder> _baseWordBuilder;
private Mock<BaseExcelBuilder> _baseExcelBuilder;
private Mock<BasePdfBuilder> _basePdfBuilder;
[OneTimeSetUp]
public void OneTimeSetUp()
{
_serviceStorageContract = new Mock<IServiceStorageContract>();
_masterStorageContract = new Mock<IMasterStorageContract>();
_orderStorageContract = new Mock<IOrderStorageContract>();
_salaryStorageContract = new Mock<ISalaryStorageContract>();
_postStorageContract = new Mock<IPostStorageContract>();
_baseWordBuilder = new Mock<BaseWordBuilder>();
_baseExcelBuilder = new Mock<BaseExcelBuilder>();
_basePdfBuilder = new Mock<BasePdfBuilder>();
}
[SetUp]
public void SetUp()
{
_serviceStorageContract.Reset();
_masterStorageContract.Reset();
_orderStorageContract.Reset();
_salaryStorageContract.Reset();
_postStorageContract.Reset();
}
[Test]
public async Task GetDataServiceHistory_ShouldSuccess_Test()
{
//Arrange
var service1Id = Guid.NewGuid().ToString();
var service2Id = Guid.NewGuid().ToString();
_serviceStorageContract.Setup(x => x.GetList()).Returns(new List<ServiceDataModel>()
{
new(service1Id, "Service 1", ServiceType.Painting, Guid.NewGuid().ToString(), 100, false),
new(service2Id, "Service 2", ServiceType.Plastering, Guid.NewGuid().ToString(), 200, false)
});
_serviceStorageContract.Setup(x => x.GetHistoryByServiceId(service1Id)).Returns(new List<ServiceHistoryDataModel>()
{
new(service1Id, 100),
new(service1Id, 120)
});
_serviceStorageContract.Setup(x => x.GetHistoryByServiceId(service2Id)).Returns(new List<ServiceHistoryDataModel>()
{
new(service2Id, 200),
new(service2Id, 250)
});
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataServiceHistoryAsync(CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(4));
_serviceStorageContract.Verify(x => x.GetList(), Times.Once);
_serviceStorageContract.Verify(x => x.GetHistoryByServiceId(service1Id), Times.Once);
_serviceStorageContract.Verify(x => x.GetHistoryByServiceId(service2Id), Times.Once);
}
[Test]
public async Task GetDataServiceHistory_WhenNoRecords_ShouldSuccess_Test()
{
//Arrange
_serviceStorageContract.Setup(x => x.GetList()).Returns(new List<ServiceDataModel>());
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataServiceHistoryAsync(CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(0));
_serviceStorageContract.Verify(x => x.GetList(), Times.Once);
}
[Test]
public void GetDataServiceHistory_WhenStorageThrowError_ShouldFail_Test()
{
//Arrange
_serviceStorageContract.Setup(x => x.GetList()).Throws(new StorageException(new InvalidOperationException()));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act&Assert
Assert.That(async () => await _reportContract.GetDataServiceHistoryAsync(CancellationToken.None), Throws.TypeOf<StorageException>());
_serviceStorageContract.Verify(x => x.GetList(), Times.Once);
}
[Test]
public async Task GetDataOrderByPeriod_ShouldSuccess_Test()
{
//Arrange
_orderStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<OrderDataModel>()
{
new(Guid.NewGuid().ToString(), DateTime.UtcNow.AddDays(-1), StatusType.InProcess, RoomType.Residential),
new(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
}));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataOrderByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(2));
_orderStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task GetDataOrderByPeriod_WhenNoRecords_ShouldSuccess_Test()
{
//Arrange
_orderStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<OrderDataModel>()));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataOrderByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(0));
_orderStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public void GetDataOrderByPeriod_WhenIncorrectDates_ShouldFail_Test()
{
//Arrange
var date = DateTime.UtcNow;
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act&Assert
Assert.That(async () => await _reportContract.GetDataOrderByPeriodAsync(date, date, CancellationToken.None), Throws.TypeOf<IncorrectDatesException>());
Assert.That(async () => await _reportContract.GetDataOrderByPeriodAsync(date, DateTime.UtcNow.AddDays(-1), CancellationToken.None), Throws.TypeOf<IncorrectDatesException>());
}
[Test]
public void GetDataOrderByPeriod_WhenStorageThrowError_ShouldFail_Test()
{
//Arrange
_orderStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Throws(new StorageException(new InvalidOperationException()));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act&Assert
Assert.That(async () => await _reportContract.GetDataOrderByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None), Throws.TypeOf<StorageException>());
_orderStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task GetDataSalaryByPeriod_ShouldSuccess_Test()
{
//Arrange
var startDate = DateTime.UtcNow.AddDays(-5);
var endDate = DateTime.UtcNow;
var master1Id = Guid.NewGuid().ToString();
var master2Id = Guid.NewGuid().ToString();
_salaryStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<SalaryDataModel>()
{
new(master1Id, startDate, 1000, 0),
new(master1Id, endDate, 1000, 0),
new(master2Id, startDate, 100, 0),
new(master2Id, endDate, 200, 0)
}));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
var mastersToReturn = new List<MasterDataModel>()
{
new(master1Id, "Иванов Иван Иванович", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-30), DateTime.UtcNow.AddYears(-5), false),
new(master2Id, "Петров Петр Петрович", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-25), DateTime.UtcNow.AddYears(-3), false)
};
_masterStorageContract.Setup(x => x.GetList(true, null, null, null, null, null)).Returns(mastersToReturn);
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataSalaryByPeriodAsync(startDate, endDate, CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(2));
var master1Salary = data.FirstOrDefault(x => x.MasterFIO == "Иванов Иван Иванович");
Assert.That(master1Salary, Is.Not.Null, "Иванов Иван Иванович not found in results");
Assert.Multiple(() =>
{
Assert.That(master1Salary.TotalSalary, Is.EqualTo(2000));
Assert.That(master1Salary.FromPeriod, Is.EqualTo(startDate));
Assert.That(master1Salary.ToPeriod, Is.EqualTo(endDate));
});
var master2Salary = data.First(x => x.MasterFIO == "Петров Петр Петрович");
Assert.That(master2Salary, Is.Not.Null);
Assert.That(master2Salary.TotalSalary, Is.EqualTo(300));
_salaryStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task GetDataSalaryByPeriod_WhenNoRecords_ShouldSuccess_Test()
{
//Arrange
_salaryStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<SalaryDataModel>()));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act
var data = await _reportContract.GetDataSalaryByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None);
//Assert
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(0));
_salaryStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public void GetDataSalaryByPeriod_WhenStorageThrowError_ShouldFail_Test()
{
//Arrange
_salaryStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>())).Throws(new StorageException(new InvalidOperationException()));
_reportContract = new ReportContract(_serviceStorageContract.Object, _masterStorageContract.Object, _orderStorageContract.Object, _salaryStorageContract.Object, _postStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
//Act&Assert
Assert.That(async () => await _reportContract.GetDataSalaryByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None), Throws.TypeOf<StorageException>());
_salaryStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
}

View File

@@ -9,19 +9,23 @@ using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.StorageContracts;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketContratcs.Infrastructure;
using TwoFromTheCasketTest.Infrastructure;
using Microsoft.Extensions.Configuration;
using TwoFromTheCasketWebApi.Infrastructure;
namespace TwoFromTheCasketTest.BuisnessLogicContractTests;
[TestFixture]
[TestFixture]
internal class SalaryBusinessLogicContractTests
{
private SalaryBusinessLogicContract _salaryBusinessLogicContract;
private Mock<ISalaryStorageContract> _salaryStorageContract;
private Mock<IOrderStorageContract> _orderStorageContract;
private Mock<IPostStorageContract> _postStorageContract;
private Mock<IMasterStorageContract> _masterStorageContract;
private readonly IConfigurationSalary _salaryConfiguration = new ConfigurationSalary(new ConfigurationBuilder().Build());
[OneTimeSetUp]
public void OneTimeSetUp()
@@ -32,7 +36,7 @@ internal class SalaryBusinessLogicContractTests
_masterStorageContract = new Mock<IMasterStorageContract>();
_salaryBusinessLogicContract = new SalaryBusinessLogicContract(_salaryStorageContract.Object,
_postStorageContract.Object, _masterStorageContract.Object,
new Mock<ILogger>().Object, _orderStorageContract.Object);
new Mock<ILogger>().Object, _orderStorageContract.Object, _salaryConfiguration);
}
[SetUp]
@@ -230,14 +234,14 @@ internal class SalaryBusinessLogicContractTests
{
//Arrange
var masterId = Guid.NewGuid().ToString();
var orderSum = 200.0;
var postSalary = 2000.0;
var rate = 2000.0;
_orderStorageContract.Setup(x => x.GetList())
.Returns([
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial)
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
]);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, postSalary));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = rate }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
@@ -245,13 +249,12 @@ internal class SalaryBusinessLogicContractTests
false)
]);
var sum = 0.0;
var expectedSum = postSalary + 500;
_salaryStorageContract.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback((SalaryDataModel x) => { sum = x.Salary; });
//Act
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert
Assert.That(sum, Is.EqualTo(expectedSum));
Assert.That(sum, Is.EqualTo(rate));
}
[Test]
@@ -272,14 +275,14 @@ internal class SalaryBusinessLogicContractTests
};
_orderStorageContract.Setup(x => x.GetList())
.Returns([
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial)
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
]);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, 2000));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = 100 }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns(list);
@@ -290,15 +293,15 @@ internal class SalaryBusinessLogicContractTests
}
[Test]
public void CalculateSalaryByMounth_WithoitOrdersByMaster_Test()
public void CalculateSalaryByMounth_WithoutOrdersByMaster_Test()
{
//Arrange
var postSalary = 2000.0;
var rate = 2000.0;
var masterId = Guid.NewGuid().ToString();
_orderStorageContract.Setup(x => x.GetList())
.Returns([]);
.Returns([]); // Нет заказов
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, postSalary));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = rate }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
@@ -306,13 +309,12 @@ internal class SalaryBusinessLogicContractTests
false)
]);
var sum = 0.0;
var expectedSum = postSalary + 500;
_salaryStorageContract.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback((SalaryDataModel x) => { sum = x.Salary; });
//Act
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert
Assert.That(sum, Is.EqualTo(expectedSum));
Assert.That(sum, Is.EqualTo(rate));
}
[Test]
@@ -320,8 +322,10 @@ internal class SalaryBusinessLogicContractTests
{
//Arrange
var masterId = Guid.NewGuid().ToString();
_orderStorageContract.Setup(x => x.GetList())
.Returns((List<OrderDataModel>)null); // Возвращаем null
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, 2000));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = 2000 }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
@@ -329,7 +333,8 @@ internal class SalaryBusinessLogicContractTests
false)
]);
//Act&Assert
Assert.DoesNotThrow(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow));
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow),
Throws.TypeOf<NullListException>());
}
[Test]
@@ -337,6 +342,10 @@ internal class SalaryBusinessLogicContractTests
{
//Arrange
var masterId = Guid.NewGuid().ToString();
_orderStorageContract.Setup(x => x.GetList())
.Returns([]); // Пустой список заказов
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns((PostDataModel)null); // Возвращаем null для поста
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
@@ -358,7 +367,7 @@ internal class SalaryBusinessLogicContractTests
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial)
]);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, 2000));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = 2000 }));
//Act&Assert
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow),
Throws.TypeOf<NullListException>());
@@ -369,8 +378,10 @@ internal class SalaryBusinessLogicContractTests
{
//Arrange
var masterId = Guid.NewGuid().ToString();
_orderStorageContract.Setup(x => x.GetList())
.Throws(new StorageException(new InvalidOperationException()));
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, 2000));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = 2000 }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
@@ -378,7 +389,8 @@ internal class SalaryBusinessLogicContractTests
false)
]);
//Act&Assert
Assert.DoesNotThrow(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow));
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow),
Throws.TypeOf<StorageException>());
}
[Test]
@@ -413,7 +425,7 @@ internal class SalaryBusinessLogicContractTests
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.Now, StatusType.Ready, RoomType.Industrial)
]);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, 2000));
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer, new PostConfiguration { Rate = 2000 }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Throws(new StorageException(new InvalidOperationException()));
@@ -421,4 +433,108 @@ internal class SalaryBusinessLogicContractTests
Assert.That(() => _salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow),
Throws.TypeOf<StorageException>());
}
[Test]
public void CalculateSalaryByMounth_WithCarpenterPostConfiguration_CalculateSalary_Test()
{
//Arrange
var masterId = Guid.NewGuid().ToString();
var rate = 2000.0;
var bonus = 50.0;
var orders = new List<OrderDataModel>()
{
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
};
_orderStorageContract.Setup(x => x.GetList())
.Returns(orders);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Carpenter,
new CarpenterPostConfiguration { Rate = rate, BonusForExtraCarpentry = bonus }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
new MasterDataModel(masterId, "А А А", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-19), DateTime.UtcNow,
false)
]);
var sum = 0.0;
var expectedSum = rate + (orders.Count * bonus); // Базовый оклад + бонус за количество заказов
_salaryStorageContract.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback((SalaryDataModel x) => { sum = x.Salary; });
//Act
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert
Assert.That(sum, Is.EqualTo(expectedSum));
}
[Test]
public void CalculateSalaryByMounth_WithPainterPostConfiguration_CalculateSalary_Test()
{
//Arrange
var masterId = Guid.NewGuid().ToString();
var rate = 2000.0;
var percent = 0.1;
var bonus = 0.5;
var orders = new List<OrderDataModel>()
{
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
};
_orderStorageContract.Setup(x => x.GetList())
.Returns(orders);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Painter,
new PainterPostConfiguration { Rate = rate, PainterPercent = percent, BonusForExtraPainter = bonus }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
new MasterDataModel(masterId, "А А А", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-19), DateTime.UtcNow,
false)
]);
var sum = 0.0;
var expectedSum = rate + (orders.Count * percent) + (orders.Count * bonus); // Базовый оклад + процент + бонус
_salaryStorageContract.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback((SalaryDataModel x) => { sum = x.Salary; });
//Act
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert
Assert.That(sum, Is.EqualTo(expectedSum));
}
[Test]
public void CalculateSalaryByMounth_WithPlastererPostConfiguration_CalculateSalary_Test()
{
//Arrange
var masterId = Guid.NewGuid().ToString();
var rate = 2000.0;
var percent = 0.1;
var bonus = 0.5;
var orders = new List<OrderDataModel>()
{
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial),
new OrderDataModel(Guid.NewGuid().ToString(), DateTime.UtcNow, StatusType.Ready, RoomType.Industrial)
};
_orderStorageContract.Setup(x => x.GetList())
.Returns(orders);
_postStorageContract.Setup(x => x.GetElementById(It.IsAny<string>()))
.Returns(new PostDataModel(Guid.NewGuid().ToString(), "name", PostType.Plasterer,
new PlastererPostConfiguration { Rate = rate, PlastererPercent = percent, BonusForExtraPlasterer = bonus }));
_masterStorageContract.Setup(x => x.GetList(It.IsAny<bool>(), It.IsAny<string?>(), It.IsAny<DateTime?>(),
It.IsAny<DateTime?>(), It.IsAny<DateTime?>(), It.IsAny<DateTime?>()))
.Returns([
new MasterDataModel(masterId, "А А А", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-19), DateTime.UtcNow,
false)
]);
var sum = 0.0;
var expectedSum = rate + (orders.Count * percent) + (orders.Count * bonus); // Базовый оклад + процент + бонус
_salaryStorageContract.Setup(x => x.AddElement(It.IsAny<SalaryDataModel>()))
.Callback((SalaryDataModel x) => { sum = x.Salary; });
//Act
_salaryBusinessLogicContract.CalculateSalaryByMonth(DateTime.UtcNow);
//Assert
Assert.That(sum, Is.EqualTo(expectedSum));
}
}

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketTest.DataModelsTest;
@@ -15,14 +16,14 @@ public class PostDataModelTests
[Test]
public void IdIsNullOrEmptyTest()
{
var post = CreateDataModel(string.Empty, "Manager", PostType.Plasterer, 50000, true, default);
var post = CreateDataModel(string.Empty, "Manager", PostType.Plasterer, new PostConfiguration { Rate = 50000 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
}
[Test]
public void IdIsNotGuidTest()
{
var post = CreateDataModel("invalid-guid", "Manager", PostType.Plasterer, 50000, true, default);
var post = CreateDataModel("invalid-guid", "Manager", PostType.Plasterer, new PostConfiguration { Rate = 50000 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
}
@@ -31,24 +32,24 @@ public class PostDataModelTests
[Test]
public void PostNameIsNullOrEmptyTest()
{
var post = CreateDataModel(Guid.NewGuid().ToString(), string.Empty, PostType.Plasterer, 50000, true, default);
var post = CreateDataModel(Guid.NewGuid().ToString(), string.Empty, PostType.Plasterer, new PostConfiguration { Rate = 50000 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
}
[Test]
public void PostTypeIsNoneTest()
{
var post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.None, 50000, true, default);
var post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.None, new PostConfiguration { Rate = 50000 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
}
[Test]
public void SalaryIsLessOrEqualToZeroTest()
{
var post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.Plasterer, 0, true, default);
var post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.Plasterer, new PostConfiguration { Rate = 0 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.Plasterer, -1000, true, default);
post = CreateDataModel(Guid.NewGuid().ToString(), "Manager", PostType.Plasterer, new PostConfiguration { Rate = -50000 });
Assert.That(() => post.Validate(), Throws.TypeOf<ValidationException>());
}
@@ -58,11 +59,11 @@ public class PostDataModelTests
var id = Guid.NewGuid().ToString();
var postName = "Manager";
var postType = PostType.Plasterer;
var salary = 50000.0;
var configuration = new PostConfiguration { Rate = 50000 };
var isActual = true;
var changeDate = DateTime.Now;
var post = CreateDataModel(id, postName, postType, salary, true, default);
var post = CreateDataModel(id, postName, postType, configuration);
Assert.That(() => post.Validate(), Throws.Nothing);
@@ -71,13 +72,13 @@ public class PostDataModelTests
Assert.That(post.Id, Is.EqualTo(id));
Assert.That(post.PostName, Is.EqualTo(postName));
Assert.That(post.PostType, Is.EqualTo(postType));
Assert.That(post.Salary, Is.EqualTo(salary));
Assert.That(post.ConfigurationModel, Is.EqualTo(configuration));
});
}
private static PostDataModel CreateDataModel(string id, string postName, PostType postType, double salary, bool isActual, DateTime changeDate)
private static PostDataModel CreateDataModel(string id, string postName, PostType postType, PostConfiguration configuration)
{
return new PostDataModel(id, postName, postType, salary);
return new PostDataModel(id, postName, postType, configuration);
}
}

View File

@@ -24,6 +24,7 @@ internal class CustomWebApplicationFactory<TProgram> : WebApplicationFactory<TPr
if (databaseConfig is not null)
services.Remove(databaseConfig);
var loggerFactory = services.SingleOrDefault(x => x.ServiceType == typeof(LoggerFactory));
if (loggerFactory is not null)
services.Remove(loggerFactory);

View File

@@ -1,5 +1,6 @@
using Microsoft.EntityFrameworkCore;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketDatabase;
using TwoFromTheCasketDatabase.Models;
@@ -19,6 +20,10 @@ internal static class TwoFromTheCasketDbContextExtensions
dbContext.ExecuteSqlRaw("TRUNCATE \"Salaries\" CASCADE;");
public static void RemoveServicesFromDatabase(this TwoFromTheCasketDbContext dbContext) =>
dbContext.ExecuteSqlRaw("TRUNCATE \"Services\" CASCADE;");
public static void RemoveServiceHistoriesFromDatabase(this TwoFromTheCasketDbContext dbContext) =>
dbContext.ExecuteSqlRaw("TRUNCATE \"ServiceHistories\" CASCADE;");
public static void RemoveServiceOrdersFromDatabase(this TwoFromTheCasketDbContext dbContext) =>
dbContext.ExecuteSqlRaw("TRUNCATE \"ServiceOrders\" CASCADE;");
public static Master InsertMasterToDatabaseAndReturn(this TwoFromTheCasketDbContext dbContext, string? id = null, string fio = "test", string? postId = null, DateTime? birthDate = null, DateTime?
employmentDate = null, bool isDeleted = false)
{
@@ -28,9 +33,8 @@ employmentDate = null, bool isDeleted = false)
FIO = fio,
PostId = postId ??
Guid.NewGuid().ToString(),
BirthDate = birthDate ?? DateTime.UtcNow.AddYears(-
20),
EmploymentDate = employmentDate ?? DateTime.UtcNow,
BirthDate = DateTime.SpecifyKind(birthDate ?? DateTime.UtcNow.AddYears(-20), DateTimeKind.Utc),
EmploymentDate = DateTime.SpecifyKind(employmentDate ?? DateTime.UtcNow, DateTimeKind.Utc),
IsDeleted = isDeleted
};
dbContext.Masters.Add(master);
@@ -42,7 +46,7 @@ employmentDate = null, bool isDeleted = false)
var order = new Order()
{
Id = id ?? Guid.NewGuid().ToString(),
Date = date ?? DateTime.UtcNow,
Date = DateTime.SpecifyKind(date ?? DateTime.UtcNow, DateTimeKind.Utc),
Status = status ?? StatusType.NotStarted,
RoomType = roomType ?? RoomType.Residential
};
@@ -51,7 +55,7 @@ employmentDate = null, bool isDeleted = false)
return order;
}
public static Post InsertPostToDatabaseAndReturn(this TwoFromTheCasketDbContext dbContext, string? id = null, string? postName = null,
PostType postType = PostType.Plasterer, double salary = 10, bool isActual = true, DateTime? changeDate = null)
PostType postType = PostType.Plasterer, PostConfiguration? configuration = null, bool isActual = true, DateTime? changeDate = null)
{
var post = new Post()
{
@@ -59,7 +63,7 @@ employmentDate = null, bool isDeleted = false)
PostId = id ?? Guid.NewGuid().ToString(),
PostName = postName ?? Guid.NewGuid().ToString(),
PostType = postType,
Salary = salary,
Configuration = configuration ?? new PostConfiguration() { Rate = 100 },
IsActual = isActual,
ChangeDate = changeDate ?? DateTime.UtcNow
};
@@ -67,11 +71,32 @@ employmentDate = null, bool isDeleted = false)
dbContext.SaveChanges();
return post;
}
//private Post InsertPostToDatabaseAndReturn(string id, string postName = "test",
// PostType postType = PostType.None, PostConfiguration? configuration = null, bool isActual = true, DateTime? changeDate = null)
//{
// var post = new Post()
// {
// Id = Guid.NewGuid().ToString(),
// PostId = id ?? Guid.NewGuid().ToString(),
// PostName = postName,
// PostType = postType,
// Configuration = configuration ?? new PostConfiguration() { Rate = 100 },
// IsActual = isActual,
// ChangeDate = changeDate ?? DateTime.UtcNow
// };
// TwoFromTheCasketDbContext.Posts.Add(post);
// TwoFromTheCasketDbContext.SaveChanges();
// return post;
//}
public static Salary InsertSalaryToDatabaseAndReturn(this TwoFromTheCasketDbContext dbContext, string masterId, double salarySize = 1,
DateTime? salaryDate = null)
{
var salary = new Salary()
{ MasterId = masterId, SalarySize = salarySize, SalaryDate = salaryDate ?? DateTime.UtcNow };
{
MasterId = masterId,
SalarySize = salarySize,
SalaryDate = DateTime.SpecifyKind(salaryDate ?? DateTime.UtcNow, DateTimeKind.Utc)
};
dbContext.Salaries.Add(salary);
dbContext.SaveChanges();
return salary;
@@ -93,4 +118,34 @@ employmentDate = null, bool isDeleted = false)
}
public static Salary[] GetSalariesFromDatabaseByEmployeeId(this TwoFromTheCasketDbContext dbContext, string id) =>
[.. dbContext.Salaries.Where(x => x.MasterId == id)];
public static Post? GetPostFromDatabaseByPostId(this TwoFromTheCasketDbContext dbContext, string id) =>
dbContext.Posts.FirstOrDefault(x => x.PostId == id && x.IsActual);
public static ServiceHistory InsertServiceHistoryToDatabaseAndReturn(this TwoFromTheCasketDbContext dbContext, string serviceId, double oldPrice = 100.0)
{
var serviceHistory = new ServiceHistory()
{
ServiceId = serviceId,
OldPrice = oldPrice,
ChangeDate = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
};
dbContext.ServiceHistories.Add(serviceHistory);
dbContext.SaveChanges();
return serviceHistory;
}
public static ServiceOrder InsertServiceOrderToDatabaseAndReturn(this TwoFromTheCasketDbContext dbContext, string orderId, string serviceId, string masterId, int timeOfWorking = 1)
{
var serviceOrder = new ServiceOrder()
{
OrderId = orderId,
ServiceId = serviceId,
MasterId = masterId,
TimeOfWorking = timeOfWorking
};
dbContext.ServiceOrders.Add(serviceOrder);
dbContext.SaveChanges();
return serviceOrder;
}
}

View File

@@ -9,6 +9,7 @@ using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketDatabase.Implementation;
using TwoFromTheCasketDatabase.Models;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
namespace TwoFromTheCasketTest.StoragesContractsTests;
@@ -123,7 +124,7 @@ internal class PostStorageContractTests : BaseStorageContractTest
[Test]
public void Try_AddElement_Test()
{
var post = CreateModel(Guid.NewGuid().ToString(), isActual: true);
var post = CreateModel(Guid.NewGuid().ToString());
_postStorageContract.AddElement(post);
AssertElement(GetPostFromDatabaseByPostId(post.Id), post);
}
@@ -131,15 +132,15 @@ internal class PostStorageContractTests : BaseStorageContractTest
[Test]
public void Try_AddElement_WhenActualIsFalse_Test()
{
var post = CreateModel(Guid.NewGuid().ToString(), isActual: false);
var post = CreateModel(Guid.NewGuid().ToString());
Assert.That(() => _postStorageContract.AddElement(post), Throws.Nothing);
AssertElement(GetPostFromDatabaseByPostId(post.Id), CreateModel(post.Id, isActual: true));
AssertElement(GetPostFromDatabaseByPostId(post.Id), CreateModel(post.Id));
}
[Test]
public void Try_AddElement_WhenHaveRecordWithSameName_Test()
{
var post = CreateModel(Guid.NewGuid().ToString(), "name unique", isActual: true);
var post = CreateModel(Guid.NewGuid().ToString(), "name unique");
InsertPostToDatabaseAndReturn(Guid.NewGuid().ToString(), postName: post.PostName, isActual: true);
Assert.That(() => _postStorageContract.AddElement(post), Throws.TypeOf<ElementExistsException>());
}
@@ -147,7 +148,7 @@ internal class PostStorageContractTests : BaseStorageContractTest
[Test]
public void Try_AddElement_WhenHaveRecordWithSamePostIdAndActualIsTrue_Test()
{
var post = CreateModel(Guid.NewGuid().ToString(), isActual: true);
var post = CreateModel(Guid.NewGuid().ToString());
InsertPostToDatabaseAndReturn(post.Id, isActual: true);
Assert.That(() => _postStorageContract.AddElement(post), Throws.TypeOf<ElementExistsException>());
}
@@ -160,17 +161,17 @@ internal class PostStorageContractTests : BaseStorageContractTest
_postStorageContract.UpdElement(post);
var posts = TwoFromTheCasketDbContext.Posts.Where(x => x.PostId == post.Id).OrderByDescending(x => x.ChangeDate);
Assert.That(posts.Count(), Is.EqualTo(2));
AssertElement(posts.First(), CreateModel(post.Id, isActual: true));
AssertElement(posts.Last(), CreateModel(post.Id, isActual: false));
AssertElement(posts.First(), CreateModel(post.Id));
AssertElement(posts.Last(), CreateModel(post.Id));
}
[Test]
public void Try_UpdElement_WhenActualIsFalse_Test()
{
var post = CreateModel(Guid.NewGuid().ToString(), isActual: false);
var post = CreateModel(Guid.NewGuid().ToString());
InsertPostToDatabaseAndReturn(post.Id, isActual: true);
_postStorageContract.UpdElement(post);
AssertElement(GetPostFromDatabaseByPostId(post.Id), CreateModel(post.Id, isActual: true));
AssertElement(GetPostFromDatabaseByPostId(post.Id), CreateModel(post.Id));
}
[Test]
@@ -251,16 +252,118 @@ internal class PostStorageContractTests : BaseStorageContractTest
Assert.That(() => _postStorageContract.ResElement(post.PostId), Throws.Nothing);
}
[Test]
public void Try_AddElement_WithPlastererPostConfiguration_Test()
{
var plastererPercent = 15.5;
var post = CreateModel(Guid.NewGuid().ToString(), config: new PlastererPostConfiguration() { Rate = 1000, PlastererPercent = plastererPercent, BonusForExtraPlasterer = 200 });
_postStorageContract.AddElement(post);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(PlastererPostConfiguration).Name));
Assert.That((element.Configuration as PlastererPostConfiguration)!.PlastererPercent, Is.EqualTo(plastererPercent));
});
}
[Test]
public void Try_AddElement_WithPainterPostConfiguration_Test()
{
var painterPercent = 20.0;
var post = CreateModel(Guid.NewGuid().ToString(), config: new PainterPostConfiguration() { Rate = 1200, PainterPercent = painterPercent, BonusForExtraPainter = 300 });
_postStorageContract.AddElement(post);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(PainterPostConfiguration).Name));
Assert.That((element.Configuration as PainterPostConfiguration)!.PainterPercent, Is.EqualTo(painterPercent));
});
}
[Test]
public void Try_AddElement_WithCarpenterPostConfiguration_Test()
{
var bonusForExtraCarpentry = 400;
var post = CreateModel(Guid.NewGuid().ToString(), config: new CarpenterPostConfiguration() { Rate = 1500, BonusForExtraCarpentry = bonusForExtraCarpentry });
_postStorageContract.AddElement(post);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(CarpenterPostConfiguration).Name));
Assert.That((element.Configuration as CarpenterPostConfiguration)!.BonusForExtraCarpentry, Is.EqualTo(bonusForExtraCarpentry));
});
}
[Test]
public void Try_UpdElement_WithPlastererPostConfiguration_Test()
{
var originalConfig = new PostConfiguration { Rate = 800 };
var post = CreateModel(Guid.NewGuid().ToString(), config: originalConfig);
InsertPostToDatabaseAndReturn(post.Id, configuration: originalConfig, isActual: true);
var plastererPercent = 18.0;
var updatedPost = CreateModel(post.Id, config: new PlastererPostConfiguration() { Rate = 1100, PlastererPercent = plastererPercent, BonusForExtraPlasterer = 250 });
_postStorageContract.UpdElement(updatedPost);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(PlastererPostConfiguration).Name));
Assert.That((element.Configuration as PlastererPostConfiguration)!.PlastererPercent, Is.EqualTo(plastererPercent));
});
}
[Test]
public void Try_UpdElement_WithPainterPostConfiguration_Test()
{
var originalConfig = new PostConfiguration { Rate = 900 };
var post = CreateModel(Guid.NewGuid().ToString(), config: originalConfig);
InsertPostToDatabaseAndReturn(post.Id, configuration: originalConfig, isActual: true);
var painterPercent = 22.5;
var updatedPost = CreateModel(post.Id, config: new PainterPostConfiguration() { Rate = 1300, PainterPercent = painterPercent, BonusForExtraPainter = 350 });
_postStorageContract.UpdElement(updatedPost);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(PainterPostConfiguration).Name));
Assert.That((element.Configuration as PainterPostConfiguration)!.PainterPercent, Is.EqualTo(painterPercent));
});
}
[Test]
public void Try_UpdElement_WithCarpenterPostConfiguration_Test()
{
var originalConfig = new PostConfiguration { Rate = 1000 };
var post = CreateModel(Guid.NewGuid().ToString(), config: originalConfig);
InsertPostToDatabaseAndReturn(post.Id, configuration: originalConfig, isActual: true);
var bonusForExtraCarpentry = 500;
var updatedPost = CreateModel(post.Id, config: new CarpenterPostConfiguration() { Rate = 1600, BonusForExtraCarpentry = bonusForExtraCarpentry });
_postStorageContract.UpdElement(updatedPost);
var element = GetPostFromDatabaseByPostId(post.Id);
Assert.That(element, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(element!.Configuration.Type, Is.EqualTo(typeof(CarpenterPostConfiguration).Name));
Assert.That((element.Configuration as CarpenterPostConfiguration)!.BonusForExtraCarpentry, Is.EqualTo(bonusForExtraCarpentry));
});
}
private Post InsertPostToDatabaseAndReturn(string id, string postName = "test",
PostType postType = PostType.None, double salary = 10, bool isActual = true, DateTime? changeDate = null)
PostType postType = PostType.None, PostConfiguration? configuration = null, bool isActual = true, DateTime? changeDate = null)
{
var post = new Post()
{
Id = Guid.NewGuid().ToString(),
PostId = id,
PostId = id ?? Guid.NewGuid().ToString(),
PostName = postName,
PostType = postType,
Salary = salary,
Configuration = configuration ?? new PostConfiguration() { Rate = 100 },
IsActual = isActual,
ChangeDate = changeDate ?? DateTime.UtcNow
};
@@ -277,14 +380,14 @@ internal class PostStorageContractTests : BaseStorageContractTest
Assert.That(actual.Id, Is.EqualTo(expected.Id));
Assert.That(actual.PostName, Is.EqualTo(expected.PostName));
Assert.That(actual.PostType, Is.EqualTo(expected.PostType));
Assert.That(actual.Salary, Is.EqualTo(expected.Salary));
Assert.That(actual.ConfigurationModel.Rate, Is.EqualTo(expected.Configuration.Rate));
});
}
private static PostDataModel CreateModel(string postId, string postName = "test",
PostType postType = PostType.None, double salary = 10, bool isActual = false, DateTime? changeDate = null)
=> new(postId, postName, postType, salary);
PostType postType = PostType.None, PostConfiguration? config = null)
=> new(postId, postName, postType, config ?? new PostConfiguration() { Rate = 100 });
private Post? GetPostFromDatabaseByPostId(string id) => TwoFromTheCasketDbContext.Posts.Where(x => x.PostId == id)
.OrderByDescending(x => x.ChangeDate).FirstOrDefault();
@@ -297,7 +400,7 @@ internal class PostStorageContractTests : BaseStorageContractTest
Assert.That(actual.PostId, Is.EqualTo(expected.Id));
Assert.That(actual.PostName, Is.EqualTo(expected.PostName));
Assert.That(actual.PostType, Is.EqualTo(expected.PostType));
Assert.That(actual.Salary, Is.EqualTo(expected.Salary));
Assert.That(actual.Configuration.Rate, Is.EqualTo(expected.ConfigurationModel.Rate));
});
}

View File

@@ -5,6 +5,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketDatabase;
using TwoFromTheCasketDatabase.Implementation;
using TwoFromTheCasketDatabase.Models;
@@ -34,10 +36,10 @@ internal class SalaryStorageContractTests : BaseStorageContractTest
[Test]
public void Try_GetList_WhenHaveRecords_Test()
{
var masterId = Guid.NewGuid().ToString();
var salary = InsertSalaryToDatabaseAndReturn(masterId, salarySize: 100);
InsertSalaryToDatabaseAndReturn(masterId);
InsertSalaryToDatabaseAndReturn(masterId);
var master = InsertMasterToDatabaseAndReturn("Test Master");
var salary = InsertSalaryToDatabaseAndReturn(master.Id, salarySize: 100);
InsertSalaryToDatabaseAndReturn(master.Id);
InsertSalaryToDatabaseAndReturn(master.Id);
var list = _salaryStorageContract.GetList(DateTime.UtcNow.AddDays(-10), DateTime.UtcNow.AddDays(10));
Assert.That(list, Is.Not.Null);
Assert.That(list, Has.Count.EqualTo(3));
@@ -103,15 +105,31 @@ internal class SalaryStorageContractTests : BaseStorageContractTest
{
var salary = CreateModel(_master.Id);
_salaryStorageContract.AddElement(salary);
_salaryStorageContract.SaveChanges();
AssertElement(GetSalaryFromDatabaseByMasterId(_master.Id), salary);
}
private Master InsertMasterToDatabaseAndReturn(string masterFIO = "fio fio fio")
{
// Сначала создаем Post
var postId = Guid.NewGuid().ToString();
var post = new Post()
{
Id = Guid.NewGuid().ToString(),
PostId = postId,
PostName = $"Test Post {Guid.NewGuid()}",
PostType = PostType.Carpenter,
Configuration = new PostConfiguration { Rate = 1000 },
IsActual = true,
ChangeDate = DateTime.UtcNow
};
TwoFromTheCasketDbContext.Posts.Add(post);
// Затем создаем Master с правильным PostId
var master = new Master()
{
Id = Guid.NewGuid().ToString(),
PostId = Guid.NewGuid().ToString(),
PostId = postId,
FIO = masterFIO,
IsDeleted = false
};

View File

@@ -18,7 +18,7 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="NUnit" Version="4.2.2" />

View File

@@ -26,27 +26,12 @@ internal class BaseWebApiControllerTest
public void OneTimeSetUp()
{
_webApplication = new CustomWebApplicationFactory<Program>();
HttpClient = _webApplication
.WithWebHostBuilder(builder =>
{
builder.ConfigureTestServices(services =>
{
using var loggerFactory = new LoggerFactory();
loggerFactory.AddSerilog(new LoggerConfiguration()
.ReadFrom.Configuration(new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build())
.CreateLogger());
services.AddSingleton(loggerFactory);
});
})
.CreateClient();
HttpClient = _webApplication.CreateClient();
var request = HttpClient.GetAsync("/login/user").GetAwaiter().GetResult();
var data = request.Content.ReadAsStringAsync().GetAwaiter().GetResult();
HttpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {data}");
TwoFromTheCasketDbContext = _webApplication.Services.GetRequiredService<TwoFromTheCasketDbContext>();
TwoFromTheCasketDbContext.Database.EnsureDeleted();
TwoFromTheCasketDbContext.Database.EnsureCreated();

View File

@@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Text.Json;
using System.Text.Json.Nodes;
using TwoFromTheCasketContratcs.BindingModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketContratcs.ViewModels;
using TwoFromTheCasketDatabase.Models;
using TwoFromTheCasketTest.Infrastructure;
@@ -55,7 +53,8 @@ internal class PostControllerTests : BaseWebApiControllerTest
Assert.That(data, Has.Count.EqualTo(0));
});
}
[Test]
public async Task GetElement_ById_WhenHaveRecord_ShouldSuccess_Test()
{
@@ -78,6 +77,7 @@ internal class PostControllerTests : BaseWebApiControllerTest
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task GetElement_ById_WhenRecordWasDeleted_ShouldNotFound_Test()
{
@@ -88,6 +88,7 @@ internal class PostControllerTests : BaseWebApiControllerTest
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task GetElement_ByName_WhenHaveRecord_ShouldSuccess_Test()
{
@@ -95,32 +96,127 @@ internal class PostControllerTests : BaseWebApiControllerTest
var postName = "TestPostName";
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postName: postName);
//Act
var response = await HttpClient.GetAsync($"/api/posts/{postName}");
var response = await HttpClient.GetAsync($"/api/posts/{Uri.EscapeDataString(postName)}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
AssertElement(await GetModelFromResponseAsync<PostViewModel>(response), post);
}
[Test]
public async Task GetElement_ByName_WhenNoRecord_ShouldNotFound_Test()
{
//Arrange
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
//Act
var response = await HttpClient.GetAsync($"/api/posts/New%20Name");
var response = await HttpClient.GetAsync($"/api/posts/{Uri.EscapeDataString("New Name")}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task GetElement_ByName_WhenRecordWasDeleted_ShouldNotFound_Test()
{
//Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(isActual: false);
//Act
var response = await HttpClient.GetAsync($"/api/posts/{post.PostName}");
var response = await HttpClient.GetAsync($"/api/posts/{Uri.EscapeDataString(post.PostName)}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
public async Task Post_ShouldSuccess_Test()
{
//Arrange
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var postModel = CreateModel();
//Act
var response = await HttpClient.PostAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
AssertElement(TwoFromTheCasketDbContext.GetPostFromDatabaseByPostId(postModel.Id!), postModel);
}
[Test]
public async Task Post_WhenHaveRecordWithSameId_ShouldBadRequest_Test()
{
//Arrange
var postModel = CreateModel();
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postModel.Id);
//Act
var response = await HttpClient.PostAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenHaveRecordWithSameName_ShouldBadRequest_Test()
{
//Arrange
var postModel = CreateModel(postName: "unique name");
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postName: postModel.PostName!);
//Act
var response = await HttpClient.PostAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenDataIsIncorrect_ShouldBadRequest_Test()
{
//Arrange
var postModelWithIdIncorrect = new PostBindingModel
{ Id = "Id", PostName = "name", PostType = PostType.Carpenter.ToString(), ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 })};
var postModelWithNameIncorrect = new PostBindingModel
{
Id = Guid.NewGuid().ToString(), PostName = string.Empty, PostType = PostType.Carpenter.ToString(),
ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 })
};
var postModelWithPostTypeIncorrect = new PostBindingModel
{ Id = Guid.NewGuid().ToString(), PostName = "name", PostType = string.Empty, ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 }) };
var postModelWithSalaryIncorrect = new PostBindingModel
{
Id = Guid.NewGuid().ToString(), PostName = "name", PostType = PostType.Carpenter.ToString(),
ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = -10 })
};
//Act
var responseWithIdIncorrect = await HttpClient.PostAsync($"/api/posts", MakeContent(postModelWithIdIncorrect));
var responseWithNameIncorrect =
await HttpClient.PostAsync($"/api/posts", MakeContent(postModelWithNameIncorrect));
var responseWithPostTypeIncorrect =
await HttpClient.PostAsync($"/api/posts", MakeContent(postModelWithPostTypeIncorrect));
var responseWithSalaryIncorrect =
await HttpClient.PostAsync($"/api/posts", MakeContent(postModelWithSalaryIncorrect));
//Assert
Assert.Multiple(() =>
{
Assert.That(responseWithIdIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Id is incorrect");
Assert.That(responseWithNameIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Name is incorrect");
Assert.That(responseWithPostTypeIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Type is incorrect");
Assert.That(responseWithSalaryIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Salary is incorrect");
});
}
[Test]
public async Task Post_WhenSendEmptyData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PostAsync($"/api/posts", MakeContent(string.Empty));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Post_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PostAsync($"/api/posts", MakeContent(new { Data = "test", Position = 10 }));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
private static void AssertElement(PostViewModel? actual, Post expected)
{
@@ -130,10 +226,22 @@ internal class PostControllerTests : BaseWebApiControllerTest
Assert.That(actual.Id, Is.EqualTo(expected.PostId));
Assert.That(actual.PostName, Is.EqualTo(expected.PostName));
Assert.That(actual.PostType, Is.EqualTo(expected.PostType.ToString()));
Assert.That(actual.Salary, Is.EqualTo(expected.Salary));
Assert.That(JsonNode.Parse(actual.Configuration)!["Rate"]!.GetValue<double>(),
Is.EqualTo(expected.Configuration.Rate));
});
}
private static PostBindingModel CreateModel(string? postId = null, string postName = "name",
PostType postType = PostType.Carpenter, string? configuration = null)
=> new()
{
Id = postId ?? Guid.NewGuid().ToString(),
PostName = postName,
PostType = postType.ToString(),
ConfigurationJson = configuration
?? JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 }),
};
private static void AssertElement(Post? actual, PostBindingModel expected)
{
Assert.That(actual, Is.Not.Null);
@@ -142,87 +250,211 @@ internal class PostControllerTests : BaseWebApiControllerTest
Assert.That(actual.PostId, Is.EqualTo(expected.Id));
Assert.That(actual.PostName, Is.EqualTo(expected.PostName));
Assert.That(actual.PostType.ToString(), Is.EqualTo(expected.PostType));
Assert.That(actual.Salary, Is.EqualTo(expected.Salary));
Assert.That(actual.Configuration.Rate, Is.EqualTo(JsonNode.Parse(expected.ConfigurationJson!)!["Rate"]!.GetValue<double>()));
});
}
private static PostBindingModel CreateModel(string? postId = null, string postName = "name",
PostType postType = PostType.Carpenter, double salary = 10)
=> new()
{
Id = postId ?? Guid.NewGuid().ToString(),
PostName = postName,
PostType = postType.ToString(),
Salary = salary
};
[Test]
public async Task Register_WithValidData_ShouldSuccess_Test()
public async Task Put_ShouldSuccess_Test()
{
// Arrange
var postBindingModel = new PostBindingModel
{
PostName = "TestPost",
PostType = PostType.Plasterer.ToString(),
Salary = 5000
};
// Act
var response = await HttpClient.PostAsync("/api/posts", MakeContent(postBindingModel));
// Assert
//Arrange
var postModel = CreateModel();
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postModel.Id);
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
TwoFromTheCasketDbContext.ChangeTracker.Clear();
AssertElement(TwoFromTheCasketDbContext.GetPostFromDatabaseByPostId(postModel.Id!), postModel);
}
[Test]
public async Task Put_WhenNoFoundRecord_ShouldBadRequest_Test()
{
//Arrange
var postModel = CreateModel();
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Register_WithInvalidData_ShouldBadRequest_Test()
public async Task Put_WhenRecordWasDeleted_ShouldBadRequest_Test()
{
// Arrange
var postBindingModel = new PostBindingModel
{
PostName = "",
PostType = PostType.Plasterer.ToString(),
Salary = -1000
};
// Act
var response = await HttpClient.PostAsync("/api/posts", MakeContent(postBindingModel));
// Assert
//Arrange
var postModel = CreateModel();
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postModel.Id, isActual: false);
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task ChangeInfo_WithValidData_ShouldSuccess_Test()
public async Task Put_WhenHaveRecordWithSameName_ShouldBadRequest_Test()
{
// Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var postBindingModel = new PostBindingModel
//Arrange
var postModel = CreateModel(postName: "unique name");
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postModel.Id);
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(postName: postModel.PostName!);
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(postModel));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Put_WhenDataIsIncorrect_ShouldBadRequest_Test()
{
//Arrange
var postModelWithIdIncorrect = new PostBindingModel
{ Id = "Id", PostName = "name", PostType = PostType.Carpenter.ToString(), ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 })};
var postModelWithNameIncorrect = new PostBindingModel
{
PostName = "UpdatedPost",
PostType = PostType.Plasterer.ToString(),
Salary = 6000
Id = Guid.NewGuid().ToString(), PostName = string.Empty, PostType = PostType.Carpenter.ToString(),
ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 })
};
// Act
var response = await HttpClient.PutAsync($"/api/posts/{post.PostId}", MakeContent(postBindingModel));
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.MethodNotAllowed));
var postModelWithPostTypeIncorrect = new PostBindingModel
{ Id = Guid.NewGuid().ToString(), PostName = "name", PostType = string.Empty, ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = 10 }) };
var postModelWithSalaryIncorrect = new PostBindingModel
{
Id = Guid.NewGuid().ToString(), PostName = "name", PostType = PostType.Carpenter.ToString(),
ConfigurationJson = JsonSerializer.Serialize(new PostConfiguration() { Rate = -10 })
};
//Act
var responseWithIdIncorrect = await HttpClient.PutAsync($"/api/posts", MakeContent(postModelWithIdIncorrect));
var responseWithNameIncorrect =
await HttpClient.PutAsync($"/api/posts", MakeContent(postModelWithNameIncorrect));
var responseWithPostTypeIncorrect =
await HttpClient.PutAsync($"/api/posts", MakeContent(postModelWithPostTypeIncorrect));
var responseWithSalaryIncorrect =
await HttpClient.PutAsync($"/api/posts", MakeContent(postModelWithSalaryIncorrect));
//Assert
Assert.Multiple(() =>
{
Assert.That(responseWithIdIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest), "Id is incorrect");
Assert.That(responseWithNameIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Name is incorrect");
Assert.That(responseWithPostTypeIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Type is incorrect");
Assert.That(responseWithSalaryIncorrect.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest),
"Salary is incorrect");
});
}
[Test]
public async Task Delete_WithValidId_ShouldSuccess_Test()
public async Task Put_WhenSendEmptyData_ShouldBadRequest_Test()
{
// Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
// Act
var response = await HttpClient.DeleteAsync($"/api/posts/{post.PostId}");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(string.Empty));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Restore_WithValidId_ShouldSuccess_Test()
public async Task Put_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
// Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(isActual: false);
// Act
var response = await HttpClient.PatchAsync($"/api/posts/{post.PostId}", null);
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
//Act
var response = await HttpClient.PutAsync($"/api/posts", MakeContent(new { Data = "test", Position = 10 }));
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Delete_ShouldSuccess_Test()
{
//Arrange
var postId = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn().PostId;
//Act
var response = await HttpClient.DeleteAsync($"/api/posts/{postId}");
//Assert
Assert.Multiple(() =>
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(TwoFromTheCasketDbContext.GetPostFromDatabaseByPostId(postId), Is.Null);
});
}
[Test]
public async Task Delete_WhenNoFoundRecord_ShouldBadRequest_Test()
{
//Arrange
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
//Act
var response = await HttpClient.DeleteAsync($"/api/posts/{Guid.NewGuid()}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Delete_WhenRecordWasDeleted_Test()
{
//Arrange
var postId = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(isActual: false).PostId;
//Act
var response = await HttpClient.DeleteAsync($"/api/posts/{postId}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Delete_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.DeleteAsync($"/api/posts/id");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Patch_ShouldSuccess_Test()
{
//Arrange
var postId = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(isActual: false).PostId;
//Act
var response = await HttpClient.PatchAsync($"/api/posts/{postId}", null);
//Assert
Assert.Multiple(() =>
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(TwoFromTheCasketDbContext.GetPostFromDatabaseByPostId(postId), Is.Not.Null);
});
}
[Test]
public async Task Patch_WhenNoFoundRecord_ShouldBadRequest_Test()
{
//Arrange
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
//Act
var response = await HttpClient.PatchAsync($"/api/posts/{Guid.NewGuid()}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Patch_WhenRecordNotWasDeleted_ShouldSuccess_Test()
{
//Arrange
var postId = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn().PostId;
//Act
var response = await HttpClient.PatchAsync($"/api/posts/{postId}", null);
//Assert
Assert.Multiple(() =>
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
Assert.That(TwoFromTheCasketDbContext.GetPostFromDatabaseByPostId(postId), Is.Not.Null);
});
}
[Test]
public async Task Patch_WhenSendWrongFormatData_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.PatchAsync($"/api/posts/id", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
}

View File

@@ -0,0 +1,410 @@
using TwoFromTheCasketContratcs.ViewModels;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketTest.Infrastructure;
using TwoFromTheCasketDatabase.Implementation;
using TwoFromTheCasketContratcs.DataModels;
using System.Net;
using Microsoft.EntityFrameworkCore;
namespace TwoFromTheCasketTest.WebApiControllersTests;
[TestFixture]
internal class ReportControllerTests : BaseWebApiControllerTest
{
[TearDown]
public void TearDown()
{
TwoFromTheCasketDbContext.RemoveServiceHistoriesFromDatabase();
TwoFromTheCasketDbContext.RemoveServicesFromDatabase();
TwoFromTheCasketDbContext.RemoveServiceOrdersFromDatabase();
TwoFromTheCasketDbContext.RemoveSalariesFromDatabase();
TwoFromTheCasketDbContext.RemoveOrdersFromDatabase();
TwoFromTheCasketDbContext.RemoveMastersFromDatabase();
TwoFromTheCasketDbContext.RemovePostsFromDatabase();
}
[Test]
public async Task GetServices_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов Иван Иванович", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров Петр Петрович", postId: post.PostId);
var service1 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Покраска стен",
masterId: master1.Id,
serviceType: ServiceType.Painting,
price: 800.0);
var service2 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Штукатурка",
masterId: master2.Id,
serviceType: ServiceType.Plastering,
price: 1500.0);
TwoFromTheCasketDbContext.ChangeTracker.Clear();
var serviceDataModel1 = new ServiceDataModel(service1.Id, service1.ServiceName, service1.ServiceType, service1.MasterId, 900.0, service1.IsDeleted);
var serviceStorageContract = new ServiceStorageContract(TwoFromTheCasketDbContext);
serviceStorageContract.UpdElement(serviceDataModel1);
TwoFromTheCasketDbContext.ChangeTracker.Clear();
var serviceDataModel2 = new ServiceDataModel(service1.Id, service1.ServiceName, service1.ServiceType, service1.MasterId, 1000.0, service1.IsDeleted);
serviceStorageContract.UpdElement(serviceDataModel2);
TwoFromTheCasketDbContext.ChangeTracker.Clear();
var startDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-30), DateTimeKind.Utc);
var endDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(1), DateTimeKind.Utc);
//Act
var response = await HttpClient.GetAsync($"/api/Report/GetServices?fromDate={startDate:yyyy-MM-dd}&toDate={endDate:yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<ServiceWithHistoryViewModel>>(response);
Assert.That(data, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(data, Has.Count.EqualTo(2));
Assert.That(data.Any(s => s.ServiceName == "Покраска стен"), Is.True);
Assert.That(data.Any(s => s.ServiceName == "Штукатурка"), Is.True);
// Проверяем, что у услуги "Покраска стен" есть история изменений
var paintingService = data.First(s => s.ServiceName == "Покраска стен");
Assert.That(paintingService.History, Has.Count.GreaterThan(0));
Assert.That(paintingService.Price, Is.EqualTo(1000.0));
});
}
[Test]
public async Task GetServices_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/GetServices?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task GetOrders_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var yesterday = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-1), DateTimeKind.Utc);
var today = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
var tomorrow = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(1), DateTimeKind.Utc);
// Создаем заказы с правильными значениями
var order1 = TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
date: yesterday,
status: StatusType.NotStarted,
roomType: RoomType.Residential);
var order2 = TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
date: today,
status: StatusType.InProcess,
roomType: RoomType.Industrial);
var url = $"/api/Report/GetOrders?fromDate={yesterday:yyyy-MM-dd}&toDate={tomorrow:yyyy-MM-dd}";
//Act
var response = await HttpClient.GetAsync(url);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<OrderViewModel>>(response);
Assert.That(data, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(data, Has.Count.EqualTo(2));
});
}
[Test]
public async Task GetOrders_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/GetOrders?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task GetSalary_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов Иван Иванович", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров Петр Петрович", postId: post.PostId);
var startDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-7), DateTimeKind.Utc);
var endDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-1), DateTimeKind.Utc);
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 100, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-10), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 1000, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 200, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master2.Id, salarySize: 500, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master2.Id, salarySize: 300, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-3), DateTimeKind.Utc));
//Act
var response = await HttpClient.GetAsync($"/api/Report/GetSalary?fromDate={startDate:yyyy-MM-dd}&toDate={endDate:yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<MasterSalaryByPeriodViewModel>>(response);
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(3));
Assert.Multiple(() =>
{
Assert.That(data.Any(x => x.MasterFIO == master1.FIO), Is.True);
Assert.That(data.Any(x => x.MasterFIO == master2.FIO), Is.True);
var master1Salaries = data.Where(x => x.MasterFIO == master1.FIO).ToList();
var master2Salaries = data.Where(x => x.MasterFIO == master2.FIO).ToList();
Assert.That(master1Salaries.Count, Is.EqualTo(1));
Assert.That(master2Salaries.Count, Is.EqualTo(2));
Assert.That(master1Salaries.First().TotalSalary, Is.EqualTo(1000));
Assert.That(master2Salaries.Any(x => x.TotalSalary == 500), Is.True);
Assert.That(master2Salaries.Any(x => x.TotalSalary == 300), Is.True);
});
}
[Test]
public async Task GetSalary_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/GetSalary?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task LoadServices_WhenHaveRecords_ShouldSuccess_Test()
{
// Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов Иван Иванович", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров Петр Петрович", postId: post.PostId);
// Создаем сервис с начальной ценой
var service1 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Покраска стен",
masterId: master1.Id,
serviceType: ServiceType.Painting,
price: 800.0);
var service2 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Штукатурка",
masterId: master2.Id,
serviceType: ServiceType.Plastering,
price: 1500.0);
TwoFromTheCasketDbContext.ChangeTracker.Clear();
var serviceDataModel1 = new ServiceDataModel(service1.Id, service1.ServiceName, service1.ServiceType, service1.MasterId, 900.0, service1.IsDeleted);
var serviceStorageContract = new ServiceStorageContract(TwoFromTheCasketDbContext);
serviceStorageContract.UpdElement(serviceDataModel1);
TwoFromTheCasketDbContext.ChangeTracker.Clear();
var startDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-30), DateTimeKind.Utc);
var endDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(1), DateTimeKind.Utc);
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadServices?fromDate={startDate:yyyy-MM-dd}&toDate={endDate:yyyy-MM-dd}");
//Assert
await AssertStreamAsync(response, "services_with_history.docx");
}
[Test]
public async Task LoadServices_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadServices?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task LoadOrders_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var yesterday = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-1), DateTimeKind.Utc);
var today = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
var tomorrow = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(1), DateTimeKind.Utc);
var post1 = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var post2 = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов Иван Иванович", postId: post1.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров Петр Петрович", postId: post2.PostId);
var service1 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Покраска стен",
masterId: master1.Id,
serviceType: ServiceType.Painting,
price: 800.0);
var service2 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Штукатурка",
masterId: master2.Id,
serviceType: ServiceType.Plastering,
price: 1500.0);
var service3 = TwoFromTheCasketDbContext.InsertServiceToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
serviceName: "Укладка плитки",
masterId: master1.Id,
serviceType: ServiceType.Carpentry,
price: 2000.0);
var order1 = TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
date: yesterday,
status: StatusType.InProcess,
roomType: RoomType.Residential);
var order2 = TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(
id: Guid.NewGuid().ToString(),
date: today,
status: StatusType.InProcess,
roomType: RoomType.Industrial);
TwoFromTheCasketDbContext.InsertServiceOrderToDatabaseAndReturn(
orderId: order1.Id,
serviceId: service1.Id,
masterId: master1.Id,
timeOfWorking: 4);
TwoFromTheCasketDbContext.InsertServiceOrderToDatabaseAndReturn(
orderId: order1.Id,
serviceId: service2.Id,
masterId: master2.Id,
timeOfWorking: 6);
TwoFromTheCasketDbContext.InsertServiceOrderToDatabaseAndReturn(
orderId: order2.Id,
serviceId: service3.Id,
masterId: master1.Id,
timeOfWorking: 8);
TwoFromTheCasketDbContext.InsertServiceOrderToDatabaseAndReturn(
orderId: order2.Id,
serviceId: service1.Id,
masterId: master1.Id,
timeOfWorking: 3);
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadOrders?fromDate={yesterday:yyyy-MM-dd}&toDate={tomorrow:yyyy-MM-dd}");
//Assert
await AssertStreamAsync(response, "orders.xlsx");
}
[Test]
public async Task LoadOrders_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadOrders?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task LoadSalary_WhenHaveRecords_ShouldSuccess_Test()
{
//Arrange
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов Иван Иванович", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров Петр Петрович", postId: post.PostId);
var startDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-7), DateTimeKind.Utc);
var endDate = DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-1), DateTimeKind.Utc);
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 100, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-10), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 1000, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master1.Id, salarySize: 200, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master2.Id, salarySize: 500, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-5), DateTimeKind.Utc));
TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master2.Id, salarySize: 300, salaryDate: DateTime.SpecifyKind(DateTime.UtcNow.AddDays(-3), DateTimeKind.Utc));
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadSalary?fromDate={startDate:yyyy-MM-dd}&toDate={endDate:yyyy-MM-dd}");
//Assert
await AssertStreamAsync(response, "salary.pdf");
}
[Test]
public async Task LoadSalary_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
//Act
var response = await HttpClient.GetAsync($"/api/Report/LoadSalary?fromDate={DateTime.UtcNow.AddDays(1):yyyy-MM-dd}&toDate={DateTime.UtcNow.AddDays(-1):yyyy-MM-dd}");
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
private static async Task AssertStreamAsync(HttpResponseMessage response, string fileNameForSave = "")
{
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
using var data = await response.Content.ReadAsStreamAsync();
Assert.That(data, Is.Not.Null);
Assert.That(data.Length, Is.GreaterThan(0));
await SaveStreamAsync(data, fileNameForSave);
}
private static async Task SaveStreamAsync(Stream stream, string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return;
}
var path = Path.Combine(Directory.GetCurrentDirectory(), fileName);
if (File.Exists(path))
{
try
{
File.Delete(path);
}
catch (IOException)
{
await Task.Delay(100);
try
{
File.Delete(path);
}
catch (IOException)
{
}
}
}
stream.Position = 0;
using var fileStream = new FileStream(path, FileMode.OpenOrCreate);
await stream.CopyToAsync(fileStream);
}
}

View File

@@ -1,4 +1,6 @@
using System.Net;
using TwoFromTheCasketContratcs.Enums;
using TwoFromTheCasketContratcs.Infrastructure.PostConfigurations;
using TwoFromTheCasketContratcs.ViewModels;
using TwoFromTheCasketTest.Infrastructure;
@@ -175,23 +177,26 @@ internal class SalaryControllerTests : BaseWebApiControllerTest
}
[Test]
public async Task Calculate_ShouldSuccess_Test()
{
{
//Arrange
var postId = Guid.NewGuid().ToString();
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId, salary: 1000);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "name", postId: postId);
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
var salary = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master.Id);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.InternalServerError));
Assert.That(salary, Has.Length.EqualTo(0));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salaries = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master.Id);
Assert.Multiple(() =>
{
Assert.That(salaries, Has.Length.EqualTo(1));
Assert.That(salaries.First().SalarySize, Is.EqualTo(100.0)); // Базовый оклад
Assert.That(salaries.First().SalaryDate.Month, Is.EqualTo(DateTime.UtcNow.Month));
});
}
@@ -200,13 +205,13 @@ internal class SalaryControllerTests : BaseWebApiControllerTest
{
// Arrange
TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn();
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "name", postId: Guid.NewGuid().ToString());
var sale = TwoFromTheCasketDbContext.InsertSalaryToDatabaseAndReturn(master.Id);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: Guid.NewGuid().ToString());
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
// Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.InternalServerError));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
}
[Test]
@@ -246,6 +251,119 @@ internal class SalaryControllerTests : BaseWebApiControllerTest
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task Calculate_WithSeveralMasters_ShouldSuccess_Test()
{
//Arrange
var postId = Guid.NewGuid().ToString();
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId);
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров П.П.", postId: post.PostId);
var master3 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Сидоров С.С.", postId: post.PostId);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salaries = TwoFromTheCasketDbContext.Salaries.ToArray();
Assert.That(salaries, Has.Length.EqualTo(3));
}
[Test]
public async Task Calculate_WithoutOrdersByMaster_ShouldSuccess_Test()
{
//Arrange
var postId = Guid.NewGuid().ToString();
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId);
var master1 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
var master2 = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Петров П.П.", postId: post.PostId);
// Не добавляем заказы для master1, добавляем для master2
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salary1 = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master1.Id).First().SalarySize;
var salary2 = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master2.Id).First().SalarySize;
Assert.That(salary1, Is.EqualTo(salary2)); // Оба получают базовый оклад
}
[Test]
public async Task Calculate_WithCarpenterPost_ShouldSuccess_Test()
{
//Arrange
var postId = Guid.NewGuid().ToString();
var configuration = new CarpenterPostConfiguration { Rate = 100, BonusForExtraCarpentry = 50 };
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId, postType: PostType.Carpenter, configuration: configuration);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salaries = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master.Id);
Assert.Multiple(() =>
{
Assert.That(salaries, Has.Length.EqualTo(1));
Assert.That(salaries.First().SalarySize, Is.GreaterThan(100.0)); // Базовый оклад + бонусы
});
}
[Test]
public async Task Calculate_WithPainterPost_ShouldSuccess_Test()
{
//Arrange
var postId = Guid.NewGuid().ToString();
var configuration = new PainterPostConfiguration { Rate = 100, PainterPercent = 0.1, BonusForExtraPainter = 25 };
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId, postType: PostType.Painter, configuration: configuration);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salaries = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master.Id);
Assert.Multiple(() =>
{
Assert.That(salaries, Has.Length.EqualTo(1));
Assert.That(salaries.First().SalarySize, Is.GreaterThan(100.0)); // Базовый оклад + процент + бонусы
});
}
[Test]
public async Task Calculate_WithPlastererPost_ShouldSuccess_Test()
{
//Arrange
var postId = Guid.NewGuid().ToString();
var configuration = new PlastererPostConfiguration { Rate = 100, PlastererPercent = 0.15, BonusForExtraPlasterer = 30 };
var post = TwoFromTheCasketDbContext.InsertPostToDatabaseAndReturn(id: postId, postType: PostType.Plasterer, configuration: configuration);
var master = TwoFromTheCasketDbContext.InsertMasterToDatabaseAndReturn(fio: "Иванов И.И.", postId: post.PostId);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
TwoFromTheCasketDbContext.InsertOrderToDatabaseAndReturn(null);
//Act
var date = Uri.EscapeDataString(DateTime.UtcNow.ToString("yyyy-MM-dd"));
var response = await HttpClient.PostAsync($"/api/salary/calculate?date={date}", null);
//Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
var salaries = TwoFromTheCasketDbContext.GetSalariesFromDatabaseByEmployeeId(master.Id);
Assert.Multiple(() =>
{
Assert.That(salaries, Has.Length.EqualTo(1));
Assert.That(salaries.First().SalarySize, Is.GreaterThan(100.0)); // Базовый оклад + процент + бонусы
});
}
[Test]
public async Task GetList_WithLargeDateRange_ShouldSuccess_Test()
{
@@ -286,4 +404,14 @@ internal class SalaryControllerTests : BaseWebApiControllerTest
Assert.That(data, Has.Count.EqualTo(0));
});
}
private static void AssertElement(SalaryViewModel? actual, TwoFromTheCasketDatabase.Models.Salary expected)
{
Assert.That(actual, Is.Not.Null);
Assert.Multiple(() =>
{
Assert.That(actual.MasterId, Is.EqualTo(expected.Master!.Id));
Assert.That(actual.SalarySize, Is.EqualTo(expected.SalarySize));
});
}
}

View File

@@ -1,4 +1,5 @@
using AutoMapper;
using System.Text.Json;
using TwoFromTheCasketContratcs.AdapterContracts;
using TwoFromTheCasketContratcs.AdapterContracts.OperationResponses;
using TwoFromTheCasketContratcs.BindingModels;
@@ -16,7 +17,7 @@ public class PostAdapter : IPostAdapter
private readonly ILogger _logger;
private readonly Mapper _mapper;
private readonly JsonSerializerOptions _jsonSerializerOptions = new() { PropertyNameCaseInsensitive = true };
public PostAdapter(IPostBuisnessLogicContract postBusinessLogicContract, ILogger<PostAdapter> logger)
{
_postBusinessLogicContract = postBusinessLogicContract;
@@ -24,7 +25,10 @@ public class PostAdapter : IPostAdapter
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<PostBindingModel, PostDataModel>();
cfg.CreateMap<PostDataModel, PostViewModel>();
cfg.CreateMap<PostDataModel, PostViewModel>()
.ForMember(x => x.Configuration, x =>
x.MapFrom(src =>
JsonSerializer.Serialize(src.ConfigurationModel, _jsonSerializerOptions)));
});
_mapper = new Mapper(config);
}

View File

@@ -0,0 +1,212 @@
using AutoMapper;
using TwoFromTheCasketContratcs.AdapterContracts;
using TwoFromTheCasketContratcs.AdapterContracts.OperationResponses;
using TwoFromTheCasketContratcs.BuisnessLogicsContracts;
using TwoFromTheCasketContratcs.DataModels;
using TwoFromTheCasketContratcs.Exceptions;
using TwoFromTheCasketContratcs.ViewModels;
namespace TwoFromTheCasketWebApi.Adapters;
public class ReportAdapter : IReportAdapter
{
private readonly IReportContract _reportContract;
private readonly ILogger _logger;
private readonly Mapper _mapper;
public ReportAdapter(IReportContract reportContract, ILogger<MasterAdapter> logger)
{
_reportContract = reportContract;
_logger = logger;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ServiceHistoryDataModel, ServiceHistoryViewModel>();
cfg.CreateMap<OrderDataModel, OrderViewModel>();
cfg.CreateMap<MasterSalaryByPeriodDataModel, MasterSalaryByPeriodViewModel>();
cfg.CreateMap<ServiceWithHistoryDataModel, ServiceWithHistoryViewModel>();
});
_mapper = new Mapper(config);
}
public async Task<ReportOperationResponse> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var stream = await _reportContract.CreateDocumentOrdersByPeriodAsync(dateStart, dateFinish, ct);
return SendStream(stream, "orders_by_period.xlsx");
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in CreateDocumentOrdersByPeriodAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in CreateDocumentOrdersByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in CreateDocumentOrdersByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in CreateDocumentOrdersByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var stream = await _reportContract.CreateDocumentSalaryByPeriodAsync(dateStart, dateFinish, ct);
return SendStream(stream, "salary_by_period.pdf");
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in CreateDocumentSalaryByPeriodAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in CreateDocumentSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in CreateDocumentSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in CreateDocumentSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var orders = await _reportContract.GetDataOrderByPeriodAsync(dateStart, dateFinish, ct);
return ReportOperationResponse.OK(orders.Select(x => _mapper.Map<OrderViewModel>(x)).ToList());
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in GetDataOrderByPeriodAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in GetDataOrderByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in GetDataOrderByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in GetDataOrderByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var salaries = await _reportContract.GetDataSalaryByPeriodAsync(dateStart, dateFinish, ct);
return ReportOperationResponse.OK(salaries.Select(x => _mapper.Map<MasterSalaryByPeriodViewModel>(x)).ToList());
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in GetDataSalaryByPeriodAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in GetDataSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in GetDataSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in GetDataSalaryByPeriodAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> GetDataServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var data = await _reportContract.GetDataServicesWithHistoryAsync(dateStart, dateFinish, ct);
var viewModels = data.Select(x => _mapper.Map<ServiceWithHistoryViewModel>(x)).ToList();
return ReportOperationResponse.OK(viewModels);
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in GetDataServicesWithHistoryAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in GetDataServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in GetDataServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in GetDataServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> CreateDocumentServicesWithHistoryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
var stream = await _reportContract.CreateDocumentServicesWithHistoryAsync(dateStart, dateFinish, ct);
return SendStream(stream, "services_with_history.docx");
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException in CreateDocumentServicesWithHistoryAsync");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException in CreateDocumentServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException in CreateDocumentServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException?.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception in CreateDocumentServicesWithHistoryAsync: {message}", ex.Message);
return ReportOperationResponse.InternalServerError(ex.Message);
}
}
private static ReportOperationResponse SendStream(Stream stream, string fileName)
{
stream.Position = 0;
return ReportOperationResponse.OK(stream, fileName);
}
}

View File

@@ -22,7 +22,9 @@ public class SalaryAdapter : ISalaryAdapter
_logger = logger;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SalaryDataModel, SalaryViewModel>();
cfg.CreateMap<SalaryDataModel, SalaryViewModel>()
.ForMember(dest => dest.SalarySize, opt => opt.MapFrom(src => src.Salary))
.ForMember(dest => dest.Id, opt => opt.Ignore());
});
_mapper = new Mapper(config);
@@ -101,9 +103,9 @@ public class SalaryAdapter : ISalaryAdapter
_salaryBusinessLogicContract.CalculateSalaryByMonth(date);
return SalaryOperationResponse.NoContent();
}
catch (NullListException)
catch (NullListException ex)
{
_logger.LogError("NullListException");
_logger.LogError(ex, "NullListException");
return SalaryOperationResponse.NotFound("The list is not initialized");
}
catch (ElementNotFoundException ex)

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TwoFromTheCasketContratcs.AdapterContracts;
namespace TwoFromTheCasketWebApi.Controllers;
[Authorize]
[Route("api/[controller]/[action]")]
[ApiController]
public class ReportController(IReportAdapter adapter) : ControllerBase
{
private readonly IReportAdapter _adapter = adapter;
[HttpGet]
[Consumes("application/json")]
public async Task<IActionResult> GetServices(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.GetDataServicesWithHistoryAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/json")]
public async Task<IActionResult> GetOrders(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.GetDataOrderByPeriodAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/json")]
public async Task<IActionResult> GetSalary(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.GetDataSalaryByPeriodAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/octet-stream")]
public async Task<IActionResult> LoadServices(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.CreateDocumentServicesWithHistoryAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/octet-stream")]
public async Task<IActionResult> LoadOrders(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.CreateDocumentOrdersByPeriodAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/octet-stream")]
public async Task<IActionResult> LoadSalary(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
var utcFromDate = DateTime.SpecifyKind(fromDate, DateTimeKind.Utc);
var utcToDate = DateTime.SpecifyKind(toDate, DateTimeKind.Utc);
return (await _adapter.CreateDocumentSalaryByPeriodAsync(utcFromDate, utcToDate, cancellationToken)).GetResponse(Request, Response);
}
}

View File

@@ -27,6 +27,7 @@ public class SalaryController(ISalaryAdapter adapter) : ControllerBase
[HttpPost]
public IActionResult Calculate(DateTime date)
{
return _adapter.CalculateSalaryByMonth(date).GetResponse(Request, Response);
var result = _adapter.CalculateSalaryByMonth(date);
return result.GetResponse(Request, Response);
}
}

View File

@@ -0,0 +1,19 @@
using TwoFromTheCasketContratcs.Infrastructure;
namespace TwoFromTheCasketWebApi.Infrastructure;
public class ConfigurationSalary(IConfiguration configuration) : IConfigurationSalary
{
private readonly Lazy<SalarySettings> _salarySettings = new(() =>
{
var settings = new SalarySettings();
configuration.GetSection("SalarySettings").Bind(settings);
return settings;
});
public double ExtraInstallationSum => _salarySettings.Value.ExtraInstallationSum;
public double ExtraSaleSum => _salarySettings.Value.ExtraSaleSum;
public int MaxThreads => _salarySettings.Value.MaxThreads;
}

View File

@@ -0,0 +1,10 @@
namespace TwoFromTheCasketWebApi.Infrastructure;
public class SalarySettings
{
public double ExtraInstallationSum { get; set; }
public double ExtraSaleSum { get; set; }
public int MaxThreads { get; set; }
}

View File

@@ -14,6 +14,7 @@ using TwoFromTheCasketWebApi.Infrastructure;
using TwoFromTheCasketBuisnessLogic.Implementations;
using TwoFromTheCasketContratcs.AdapterContracts;
using TwoFromTheCasketWebApi.Adapters;
using TwoFromTheCasketBuisnessLogic.OfficePackage;
var builder = WebApplication.CreateBuilder(args);
@@ -32,17 +33,18 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidIssuer = AuthOptions.ISSUER, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateAudience = true, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidAudience = AuthOptions.AUDIENCE, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateLifetime = true, // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), // <20><><EFBFBD><EFBFBD>
ValidateIssuerSigningKey = true, // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateIssuer = true, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidIssuer = AuthOptions.ISSUER, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateAudience = true, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidAudience = AuthOptions.AUDIENCE, // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ValidateLifetime = true, // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(), // <20><><EFBFBD><EFBFBD>
ValidateIssuerSigningKey = true, // <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
};
});
builder.Services.AddSingleton<IConfigurationDatabase, ConfigurationDatabase>();
builder.Services.AddSingleton<IConfigurationSalary, ConfigurationSalary>();
builder.Services.AddTransient<IMasterBuisnessLogicContract, MasterBusinessLogicContract>();
builder.Services.AddTransient<IOrderBuisnessLogicContract, OrderBusinessLogicContract>();
@@ -63,6 +65,14 @@ builder.Services.AddTransient<IMasterAdapter, MasterAdapter>();
builder.Services.AddTransient<ISalaryAdapter, SalaryAdapter>();
builder.Services.AddTransient<IServiceAdapter, ServiceAdapter>();
// Office Package builders
builder.Services.AddTransient<BaseWordBuilder, OpenXmlWordBuilder>();
builder.Services.AddTransient<BaseExcelBuilder, OpenXmlExcelBuilder>();
builder.Services.AddTransient<BasePdfBuilder, MigraDocPdfBuilder>();
builder.Services.AddTransient<IReportContract, ReportContract>();
builder.Services.AddTransient<IReportAdapter, ReportAdapter>();
builder.Services.AddOpenApi();
var app = builder.Build();

View File

@@ -7,8 +7,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
</ItemGroup>

View File

@@ -4,5 +4,10 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"SalarySettings": {
"ExtraInstallationSum": 1000.0,
"ExtraSaleSum": 5000.0,
"MaxThreads": 4
}
}

View File

@@ -26,5 +26,10 @@
"AllowedHosts": "*",
"DataBaseSettings": {
"ConnectionString": "Host=127.0.0.1;Port=5432;Database=TwoFromTheCasket;Username=postgres;Password=123;"
},
"SalarySettings": {
"ExtraInstallationSum": 1000.0,
"ExtraSaleSum": 5000.0,
"MaxThreads": 4
}
}