all done lab_6

This commit is contained in:
2025-04-22 23:47:23 +04:00
parent 04966ef9e5
commit 29d974f2c1
17 changed files with 594 additions and 19 deletions

View File

@@ -9,13 +9,20 @@ using SmallSoftwareContracts.StoragesContracts;
namespace SmallSoftwareBusinessLogic.Implementations;
internal class ReportContract(ISoftwareStorageContract softwareStorageContract,
IRequestStorageContract requestStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, ILogger logger) : IReportContract
IRequestStorageContract requestStorageContract,
ISalaryStorageContract salaryStorageContract,
BaseWordBuilder baseWordBuilder,
BaseExcelBuilder baseExcelBuilder,
BasePdfBuilder basePdfBuilder, ILogger logger) : IReportContract
{
private readonly ISoftwareStorageContract _softwareStorageContract = softwareStorageContract;
private readonly ILogger _logger = logger;
private readonly IRequestStorageContract _requestStorageContract = requestStorageContract;
private readonly ISalaryStorageContract _salaryStorageContract = salaryStorageContract;
private readonly BaseWordBuilder _baseWordBuilder = baseWordBuilder;
private readonly BaseExcelBuilder _baseExcelBuilder = baseExcelBuilder;
private readonly ILogger _logger = logger;
private readonly BasePdfBuilder _basePdfBuilder = basePdfBuilder;
internal static readonly string[] tableHeader = ["Дата", "Сумма", "Товар", "Кол-во"];
internal static readonly string[] documentHeader = ["Название ПО", "Старая цена", "Дата"];
@@ -118,4 +125,28 @@ internal class ReportContract(ISoftwareStorageContract softwareStorageContract,
.Build();
}
public Task<List<WorkerSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
logger.LogInformation("Get data SalaryByPeriod from {dateStart} to { dateFinish}", dateStart, dateFinish);
return GetDataBySalaryAsync(dateStart, dateFinish, ct);
}
private async Task<List<WorkerSalaryByPeriodDataModel>> GetDataBySalaryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish);
}
return [.. (await _salaryStorageContract.GetListAsync(dateStart, dateFinish, ct)).GroupBy(x => x.WorkerFIO).Select(x => new
WorkerSalaryByPeriodDataModel { WorkerFIO = x.Key, TotalSalary = x.Sum(y => y.Salary),
FromPeriod = x.Min(y => y.SalaryDate), ToPeriod = x.Max(y => y.SalaryDate) }).OrderBy(x => x.WorkerFIO)];
}
public async Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report SalaryByPeriod from {dateStart} to { dateFinish}", dateStart, dateFinish);
var data = await GetDataBySalaryAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
return _basePdfBuilder.AddHeader("Зарплатная ведомость").AddParagraph($"за период с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}")
.AddPieChart("Начисления", [.. data.Select(x => (x.WorkerFIO, x.TotalSalary))]).Build();
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SmallSoftwareBusinessLogic.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 Stream Build();
}

View File

@@ -0,0 +1,76 @@
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
using MigraDoc.Rendering;
using System.Text;
namespace SmallSoftwareBusinessLogic.OfficePackage;
public 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 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 headerStyle = _document.Styles.AddStyle("NormalBold", "Normal");
headerStyle.Font.Bold = true;
headerStyle.Font.Size = 14;
}
}

View File

@@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup>
<ItemGroup>

View File

@@ -8,5 +8,6 @@ public interface IReportAdapter
Task<ReportOperationResponse> CreateDocumentSoftwaresByManufacturerAsync(CancellationToken ct);
Task<ReportOperationResponse> GetDataRequestByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentRequestsByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -11,4 +11,5 @@ public class ReportOperationResponse : OperationResponse
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);
public static ReportOperationResponse OK(List<WorkerSalaryByPeriodViewModel> data) => OK<ReportOperationResponse, List<WorkerSalaryByPeriodViewModel>>(data);
}

View File

@@ -8,6 +8,6 @@ public interface IReportContract
Task<List<RequestDataModel>> GetDataRequestByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentSoftwaresByHistoryAsync(CancellationToken ct);
Task<Stream> CreateDocumentRequestsByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<WorkerSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,10 @@
namespace SmallSoftwareContracts.DataModels;
public class WorkerSalaryByPeriodDataModel
{
public required string WorkerFIO { get; set; }
public double TotalSalary { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -6,5 +6,6 @@ public interface ISalaryStorageContract
{
List<SalaryDataModel> GetList(DateTime startDate, DateTime endDate, string? workerId = null);
void AddElement(SalaryDataModel salaryDataModel);
Task<List<SalaryDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
}

View File

@@ -0,0 +1,9 @@
namespace SmallSoftwareContracts.ViewModels;
public class WorkerSalaryByPeriodViewModel
{
public required string WorkerFIO { get; set; }
public double TotalSalary { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -53,4 +53,17 @@ internal class SalaryStorageContract : ISalaryStorageContract
throw new StorageException(ex);
}
}
public async Task<List<SalaryDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
return [.. await _dbContext.Salaries.Include(x => x.Worker).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

@@ -19,6 +19,7 @@ internal class ReportContractTests
private Mock<ISalaryStorageContract> _salaryStorageContract;
private Mock<BaseWordBuilder> _baseWordBuilder;
private Mock<BaseExcelBuilder> _baseExcelBuilder;
private Mock<BasePdfBuilder> _basePdfBuilder;
[OneTimeSetUp]
public void OneTimeSetUp()
{
@@ -26,8 +27,9 @@ internal class ReportContractTests
_requestStorageContract = new Mock<IRequestStorageContract>();
_salaryStorageContract = new Mock<ISalaryStorageContract>();
_baseWordBuilder = new Mock<BaseWordBuilder>();
_baseExcelBuilder = new Mock<BaseExcelBuilder>();
_reportContract = new ReportContract(_softwareStorageContract.Object, _requestStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, new Mock<ILogger>().Object);
_baseExcelBuilder = new Mock<BaseExcelBuilder>();
_basePdfBuilder = new Mock<BasePdfBuilder>();
_reportContract = new ReportContract(_softwareStorageContract.Object, _requestStorageContract.Object, _salaryStorageContract.Object, _baseWordBuilder.Object, _baseExcelBuilder.Object, _basePdfBuilder.Object, new Mock<ILogger>().Object);
}
[SetUp]
public void SetUp()
@@ -229,6 +231,119 @@ internal class ReportContractTests
It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
}
[Test]
public async Task GetDataSalaryByPeriod_ShouldSuccess_Test()
{
// Arrange
var startDate = DateTime.UtcNow.AddDays(-20);
var endDate = DateTime.UtcNow.AddDays(5);
var worker1 = new WorkerDataModel(
Guid.NewGuid().ToString(),
"fio 1",
Guid.NewGuid().ToString(),
DateTime.UtcNow.AddYears(-20),
DateTime.UtcNow.AddDays(-3));
var worker2 = new WorkerDataModel(
Guid.NewGuid().ToString(),
"fio 2",
Guid.NewGuid().ToString(),
DateTime.UtcNow.AddYears(-20),
DateTime.UtcNow.AddDays(-3));
_salaryStorageContract.Setup(x =>
x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<SalaryDataModel>()
{
new(worker1.Id, DateTime.UtcNow.AddDays(-10), 100),
new(worker1.Id, endDate, 1000),
new(worker1.Id, startDate, 1000),
new(worker2.Id, DateTime.UtcNow.AddDays(-10), 100),
new(worker2.Id, DateTime.UtcNow.AddDays(-5), 200)
});
// 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(1));
var totalSalary = data.First();
Assert.Multiple(() =>
{
Assert.That(totalSalary, Is.Not.Null);
Assert.That(totalSalary.TotalSalary, Is.EqualTo(2400));
});
_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>()))
.ReturnsAsync(new List<SalaryDataModel>());
// 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_WhenIncorrectDates_ShouldFail_Test()
{
// Arrange
var date = DateTime.UtcNow;
// Act & Assert
Assert.That(async () => await _reportContract.GetDataSalaryByPeriodAsync(
date, date, CancellationToken.None),
Throws.TypeOf<IncorrectDatesException>());
Assert.That(async () => await _reportContract.GetDataSalaryByPeriodAsync(
date, DateTime.UtcNow.AddDays(-1), CancellationToken.None),
Throws.TypeOf<IncorrectDatesException>());
}
[Test]
public void GetDataBySalaryByPeriod_WhenStorageThrowError_ShouldFail_Test()
{
// Arrange
_salaryStorageContract.Setup(x =>
x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new StorageException(new InvalidOperationException()));
// 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);
}
[Test]
public async Task CreateDocumentRequestsByPeriod_ShouldSuccess_Test()
{
@@ -335,4 +450,60 @@ internal class ReportContractTests
_baseExcelBuilder.Verify(x => x.Build(), Times.Once);
}
[Test]
public async Task CreateDocumentSalaryByPeriod_ShouldeSuccess_Test()
{
//Arrange
var startDate = DateTime.UtcNow.AddDays(-20);
var endDate = DateTime.UtcNow.AddDays(5);
var worker1 = new WorkerDataModel(Guid.NewGuid().ToString(), "fio 1", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-20), DateTime.UtcNow.AddDays(-3));
var worker2 = new WorkerDataModel(Guid.NewGuid().ToString(), "fio 2", Guid.NewGuid().ToString(), DateTime.UtcNow.AddYears(-20), DateTime.UtcNow.AddDays(-3));
_salaryStorageContract.Setup(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(),
It.IsAny<CancellationToken>())).Returns(Task.FromResult(new List<SalaryDataModel>()
{
new(worker1.Id, DateTime.UtcNow.AddDays(-10), 100, worker1),
new(worker1.Id, endDate, 1000, worker1),
new(worker1.Id, startDate, 1000, worker1),
new(worker2.Id, DateTime.UtcNow.AddDays(-10), 100, worker2),
new(worker2.Id, DateTime.UtcNow.AddDays(-5), 200, worker2)
}));
_basePdfBuilder.Setup(x => x.AddHeader(It.IsAny<string>())).Returns(_basePdfBuilder.Object);
_basePdfBuilder.Setup(x => x.AddParagraph(It.IsAny<string>())).Returns(_basePdfBuilder.Object);
var countRows = 0;
(string, double) firstRow = default;
(string, double) secondRow = default;
_basePdfBuilder.Setup(x => x.AddPieChart(It.IsAny<string>(), It.IsAny<List<(string, double)>>())).Callback((string header, List<(string, double)> data) =>
{
countRows = data.Count;
firstRow = data[0];
secondRow = data[1];
}).Returns(_basePdfBuilder.Object);
//Act
var data = await _reportContract.CreateDocumentSalaryByPeriodAsync(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow, CancellationToken.None);
//Assert
Assert.Multiple(() =>
{
Assert.That(countRows, Is.EqualTo(2));
Assert.That(firstRow, Is.Not.EqualTo(default));
Assert.That(secondRow, Is.Not.EqualTo(default));
});
Assert.Multiple(() =>
{
Assert.That(firstRow.Item1, Is.EqualTo(worker1.FIO));
Assert.That(firstRow.Item2, Is.EqualTo(2100));
Assert.That(secondRow.Item1, Is.EqualTo(worker2.FIO));
Assert.That(secondRow.Item2, Is.EqualTo(300));
});
_salaryStorageContract.Verify(x => x.GetListAsync(It.IsAny<DateTime>(), It.IsAny<DateTime>(), It.IsAny<CancellationToken>()), Times.Once);
_basePdfBuilder.Verify(x => x.AddHeader(It.IsAny<string>()), Times.Once);
_basePdfBuilder.Verify(x => x.AddParagraph(It.IsAny<string>()), Times.Once);
_basePdfBuilder.Verify(x => x.AddPieChart(It.IsAny<string>(), It.IsAny<List<(string, double)>>()), Times.Once);
_basePdfBuilder.Verify(x => x.Build(), Times.Once);
}
}

View File

@@ -113,7 +113,35 @@ internal class SalaryStorageContractTests : BaseStorageContractTest
Assert.That(list, Has.Count.EqualTo(2));
Assert.That(list.All(x => x.WorkerId == _worker.Id));
});
}
}
[Test]
public async Task Try_GetListAsync_ByPeriod_Test()
{
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(-2));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(-1).AddMinutes(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(-1).AddMinutes(5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(1).AddMinutes(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(1).AddMinutes(5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(_worker.Id, salaryDate: DateTime.UtcNow.AddDays(-2));
var list = await _salaryStorageContract.GetListAsync(DateTime.UtcNow.AddDays(-1),
DateTime.UtcNow.AddDays(1), CancellationToken.None);
Assert.That(list, Is.Not.Null);
Assert.Multiple(() => {
Assert.That(list, Has.Count.EqualTo(2));
});
}
[Test]
public async Task Try_GetListAsync_WhenNoRecords_Test()
{
var list = await _salaryStorageContract.GetListAsync(DateTime.UtcNow.AddDays(-10),
DateTime.UtcNow.AddDays(10), CancellationToken.None);
Assert.That(list, Is.Not.Null);
Assert.That(list, Is.Empty);
}
[Test]
public void Try_AddElement_Test()
{

View File

@@ -29,7 +29,6 @@ internal class ReportControllerTests : BaseWebApiControllerTest
SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturer1.Id, manufacturerName: manufacturer1.ManufacturerName);
SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturer2.Id, manufacturerName: manufacturer2.ManufacturerName);
var software1 = new SoftwareDataModel(Guid.NewGuid().ToString(), "soft1", SoftwareType.AudioDriver, manufacturer1.Id, 10, false);
var software2 = new SoftwareDataModel(Guid.NewGuid().ToString(), "soft2", SoftwareType.AudioDriver, manufacturer2.Id, 10, false);
@@ -94,25 +93,109 @@ internal class ReportControllerTests : BaseWebApiControllerTest
Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task GetSalary_WhenHaveRecords_ShouldSuccess_Test()
{
// Arrange
var post = SmallSoftwareDbContext.InsertPostToDatabaseAndReturn();
var worker1 = SmallSoftwareDbContext.InsertWorkerToDatabaseAndReturn(fio: "fio 1", postId: post.PostId).AddPost(post);
var worker2 = SmallSoftwareDbContext.InsertWorkerToDatabaseAndReturn(fio: "fio 2", postId: post.PostId).AddPost(post);
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 100,
salaryDate: DateTime.UtcNow.AddDays(-10));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 1000,
salaryDate: DateTime.UtcNow.AddDays(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 200,
salaryDate: DateTime.UtcNow.AddDays(5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker2.Id,
workerSalary: 500,
salaryDate: DateTime.UtcNow.AddDays(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker2.Id,
workerSalary: 300,
salaryDate: DateTime.UtcNow.AddDays(-3));
// Act
var response = await HttpClient.GetAsync(
$"/api/report/getsalary?" +
$"fromDate={DateTime.UtcNow.AddDays(-7):MM/dd/yyyy HH:mm:ss}&" +
$"toDate={DateTime.UtcNow.AddDays(-1):MM/dd/yyyy HH:mm:ss}");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var data = await GetModelFromResponseAsync<List<WorkerSalaryByPeriodViewModel>>(response);
Assert.That(data, Is.Not.Null);
Assert.That(data, Has.Count.EqualTo(2));
Assert.Multiple(() =>
{
Assert.That(
data.First(x => x.WorkerFIO == worker1.FIO).TotalSalary,
Is.EqualTo(1000));
Assert.That(
data.First(x => x.WorkerFIO == worker2.FIO).TotalSalary,
Is.EqualTo(800));
});
}
[Test]
public async Task GetSalary_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
// Act
var response = await HttpClient.GetAsync(
$"/api/report/getsalary?" +
$"fromDate={DateTime.UtcNow.AddDays(1):MM/dd/yyyy HH:mm:ss}&" +
$"toDate={DateTime.UtcNow.AddDays(-1):MM/dd/yyyy HH:mm:ss}");
// Assert
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task LoadSoftwares_WhenHaveRecords_ShouldSuccess_Test()
{
{
//Arrange
var manufacturer1 = SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturerName: "name 1");
var manufacturer2 = SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturerName: "name 2");
SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer1.Id, softwareName: "name 1.1");
SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer1.Id, softwareName: "name 1.2");
SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer1.Id, softwareName: "name 1.3");
SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer2.Id, softwareName: "name 2.1");
SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer2.Id, softwareName: "name 2.2");
var manufacturer1 = new ManufacturerDataModel(Guid.NewGuid().ToString(), "name1");
var manufacturer2 = new ManufacturerDataModel(Guid.NewGuid().ToString(), "name2");
SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturer1.Id, manufacturerName: manufacturer1.ManufacturerName);
SmallSoftwareDbContext.InsertManufacturerToDatabaseAndReturn(manufacturer2.Id, manufacturerName: manufacturer2.ManufacturerName);
var softwareId1 = Guid.NewGuid().ToString();
var softwareId2 = Guid.NewGuid().ToString();
var software1 = SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer1.Id,softwareId1, "name1", SoftwareType.Windows, 10, false);
var software2 = SmallSoftwareDbContext.InsertSoftwareToDatabaseAndReturn(manufacturer2.Id, softwareId2, "name2", SoftwareType.Windows, 10, false);
SmallSoftwareDbContext.InsertSoftwareHistoryToDatabaseAndReturn(softwareId1, 22, DateTime.UtcNow);
SmallSoftwareDbContext.InsertSoftwareHistoryToDatabaseAndReturn(softwareId2, 21, DateTime.UtcNow);
SmallSoftwareDbContext.InsertSoftwareHistoryToDatabaseAndReturn(softwareId1, 33, DateTime.UtcNow);
SmallSoftwareDbContext.InsertSoftwareHistoryToDatabaseAndReturn(softwareId1, 32, DateTime.UtcNow);
SmallSoftwareDbContext.InsertSoftwareHistoryToDatabaseAndReturn(softwareId2, 65, DateTime.UtcNow);
//Act
var response = await HttpClient.GetAsync("/api/report/LoadSoftwares");
//Assert
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 AssertStreamAsync(response, "file.docx");
}
[Test]
public async Task LoadRequests_WhenHaveRecords_ShouldSuccess_Test()
@@ -164,6 +247,68 @@ internal class ReportControllerTests : BaseWebApiControllerTest
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task LoadSalary_WhenHaveRecords_ShouldSuccess_Test()
{
// Arrange
var post = SmallSoftwareDbContext.InsertPostToDatabaseAndReturn();
var worker1 = SmallSoftwareDbContext.InsertWorkerToDatabaseAndReturn(
fio: "fio 1",
postId: post.PostId).AddPost(post);
var worker2 = SmallSoftwareDbContext.InsertWorkerToDatabaseAndReturn(
fio: "fio 2",
postId: post.PostId).AddPost(post);
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 100,
salaryDate: DateTime.UtcNow.AddDays(-10));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 1000,
salaryDate: DateTime.UtcNow.AddDays(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker1.Id,
workerSalary: 200,
salaryDate: DateTime.UtcNow.AddDays(5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker2.Id,
workerSalary: 500,
salaryDate: DateTime.UtcNow.AddDays(-5));
SmallSoftwareDbContext.InsertSalaryToDatabaseAndReturn(
worker2.Id,
workerSalary: 300,
salaryDate: DateTime.UtcNow.AddDays(-3));
// Act
var response = await HttpClient.GetAsync(
$"/api/report/loadsalary?" +
$"fromDate={DateTime.UtcNow.AddDays(-7):MM/dd/yyyy HH:mm:ss}&" +
$"toDate={DateTime.UtcNow.AddDays(-1):MM/dd/yyyy HH:mm:ss}");
// Assert
await AssertStreamAsync(response, "file.pdf");
}
[Test]
public async Task LoadSalary_WhenDateIsIncorrect_ShouldBadRequest_Test()
{
// Act
var response = await HttpClient.GetAsync(
$"/api/report/loadsalary?" +
$"fromDate={DateTime.UtcNow.AddDays(1):MM/dd/yyyy HH:mm:ss}&" +
$"toDate={DateTime.UtcNow.AddDays(-1):MM/dd/yyyy HH:mm:ss}");
// 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));

View File

@@ -23,6 +23,7 @@ public class ReportAdapter : IReportAdapter
cfg.CreateMap<HistoryOfSoftwareDataModel, HistoryOfSoftwareViewModel>();
cfg.CreateMap<RequestDataModel, RequestViewModel>();
cfg.CreateMap<InstallationRequestDataModel, InstallationRequestViewModel>();
cfg.CreateMap<WorkerSalaryByPeriodDataModel, WorkerSalaryByPeriodViewModel>();
});
_mapper = new Mapper(config);
@@ -146,4 +147,61 @@ public class ReportAdapter : IReportAdapter
return ReportOperationResponse.OK(stream, fileName);
}
public async Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
return ReportOperationResponse.OK((await _reportContract.GetDataSalaryByPeriodAsync(dateStart, dateFinish, ct))
.Select(x => _mapper.Map<WorkerSalaryByPeriodViewModel>(x)).ToList());
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message}");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return
ReportOperationResponse.InternalServerError(ex.Message);
}
}
public async Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
try
{
return SendStream(await _reportContract.CreateDocumentSalaryByPeriodAsync(dateStart, dateFinish, ct), "salary.pdf");
}
catch (IncorrectDatesException ex)
{
_logger.LogError(ex, "IncorrectDatesException");
return ReportOperationResponse.BadRequest($"Incorrect dates: {ex.Message} ");
}
catch (InvalidOperationException ex)
{
_logger.LogError(ex, "InvalidOperationException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (StorageException ex)
{
_logger.LogError(ex, "StorageException");
return ReportOperationResponse.InternalServerError($"Error while working with data storage: {ex.InnerException!.Message}");
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception");
return
ReportOperationResponse.InternalServerError(ex.Message);
}
}
}

View File

@@ -36,11 +36,25 @@ public class ReportController(IReportAdapter adapter) : ControllerBase
[HttpGet]
[Consumes("application/octet-stream")]
public async Task<IActionResult> LoadRequests(DateTime fromDate, DateTime
toDate, CancellationToken cancellationToken)
public async Task<IActionResult> LoadRequests(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
return (await _adapter.CreateDocumentRequestsByPeriodAsync(fromDate,
toDate, cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/json")]
public async Task<IActionResult> GetSalary(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
return (await _adapter.GetDataSalaryByPeriodAsync(fromDate, toDate,
cancellationToken)).GetResponse(Request, Response);
}
[HttpGet]
[Consumes("application/octet-stream")]
public async Task<IActionResult> LoadSalary(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken)
{
return (await _adapter.CreateDocumentSalaryByPeriodAsync(fromDate, toDate, cancellationToken)).GetResponse(Request, Response);
}
}

View File

@@ -78,6 +78,7 @@ builder.Services.AddTransient<IReportContract, ReportContract>();
builder.Services.AddTransient<IReportAdapter, ReportAdapter>();
builder.Services.AddTransient<BaseWordBuilder, OpenXmlWordBuilder>();
builder.Services.AddTransient<BaseExcelBuilder, OpenXmlExcelBuilder>();
builder.Services.AddTransient<BasePdfBuilder, MigraDocPdfBuilder>();
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();