40 Commits

Author SHA1 Message Date
af440f46e0 CustomMapper 2025-04-21 19:57:47 +04:00
77909ba831 locale en 2025-04-10 21:04:43 +04:00
212527d3de check locale 2025-04-10 20:56:45 +04:00
94d09f33b6 strings 2025-04-10 20:39:32 +04:00
d397dcd2cc Dates 2025-04-10 17:47:46 +04:00
f7444f5582 Numbers 2025-04-10 17:05:10 +04:00
2f383752ea Save files on tests 2025-04-06 16:46:21 +04:00
02b8c2ceeb CreateDocumentSalaryByPeriod 2025-04-06 16:30:49 +04:00
3a87f41323 GetDataSalaryByPeriod 2025-04-06 16:02:57 +04:00
d2cba3bbca CreateDocumentSalesByPeriod 2025-04-06 15:32:36 +04:00
008f5001a3 GetDataSaleByPeriod 2025-04-06 15:00:04 +04:00
72a0aeb96a CreateDocumentProductsByManufacturer 2025-04-06 14:19:33 +04:00
dead17cca0 GetDataProductsByManufacturer 2025-04-06 13:41:29 +04:00
e561eb91d3 Prepare 2025-04-06 13:07:41 +04:00
f90274ff5e миграция AddDateOfDeleteInWorker 2025-03-28 20:23:37 +04:00
1807aabf23 Расчет зарплаты 2025-03-27 20:32:15 +04:00
51ec839e90 Изменения в должности 2025-03-27 19:58:54 +04:00
6752ec0292 Первая миграция 2025-03-27 19:05:24 +04:00
afe3a18866 Работник 2025-03-09 20:36:28 +04:00
f3449dee3c Продажа 2025-03-09 19:47:33 +04:00
0ff65a7faa Revert "Зарплата"
This reverts commit e40e9edd5d.
2025-03-09 18:47:08 +04:00
e40e9edd5d Зарплата 2025-03-09 18:43:54 +04:00
efef7afd91 Товар 2025-03-09 17:46:36 +04:00
5c978beafa Должность 2025-03-09 17:02:45 +04:00
1b3e780fb7 Производитель 2025-03-09 16:18:45 +04:00
72547eeb41 Тесты контрактов хранилищ 2025-03-09 16:04:50 +04:00
9ed1d7b442 Основа + покупатель 2025-03-09 10:53:04 +04:00
ba060a7e21 Тесты 2025-02-17 21:07:05 +04:00
3b5ffb40e1 Контракты 2025-02-17 20:29:08 +04:00
9ebecdc818 DbContext 2025-02-17 19:45:57 +04:00
4a82378f01 Проект и модели 2025-02-17 19:36:37 +04:00
9b7afd24b1 мелкие правки 2025-02-06 09:54:14 +04:00
d8ccc037d3 Реалзиации 2025-02-05 20:43:37 +04:00
aa1d5d3113 Тесты 2025-02-05 20:43:29 +04:00
c86d2e9c21 Расширение 2025-02-05 20:43:15 +04:00
1ca954dfb8 Исключения 2025-02-05 20:43:06 +04:00
ffe137d574 Конструкторы 2025-02-05 20:20:51 +04:00
6fd6848c7a Изменение по должности 2025-02-05 18:06:07 +04:00
92377771a3 Заготовки реализаций 2025-02-05 18:03:41 +04:00
b98947dd4b Контракты 2025-02-05 17:52:18 +04:00
195 changed files with 18197 additions and 250 deletions

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="CatHasPawsWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CatHasPawsContratcs\CatHasPawsContratcs.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,73 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace CatHasPawsBusinessLogic.Implementations;
internal class BuyerBusinessLogicContract(IBuyerStorageContract buyerStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IBuyerBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IBuyerStorageContract _buyerStorageContract = buyerStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<BuyerDataModel> GetAllBuyers()
{
_logger.LogInformation("GetAllBuyers");
return _buyerStorageContract.GetList();
}
public BuyerDataModel GetBuyerByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _buyerStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
if (Regex.IsMatch(data, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
{
return _buyerStorageContract.GetElementByPhoneNumber(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _buyerStorageContract.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertBuyer(BuyerDataModel buyerDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(buyerDataModel));
ArgumentNullException.ThrowIfNull(buyerDataModel);
buyerDataModel.Validate(_localizer);
_buyerStorageContract.AddElement(buyerDataModel);
}
public void UpdateBuyer(BuyerDataModel buyerDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(buyerDataModel));
ArgumentNullException.ThrowIfNull(buyerDataModel);
buyerDataModel.Validate(_localizer);
_buyerStorageContract.UpdElement(buyerDataModel);
}
public void DeleteBuyer(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_buyerStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,69 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace CatHasPawsBusinessLogic.Implementations;
internal class ManufacturerBusinessLogicContract(IManufacturerStorageContract manufacturerStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IManufacturerBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IManufacturerStorageContract _manufacturerStorageContract = manufacturerStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<ManufacturerDataModel> GetAllManufacturers()
{
_logger.LogInformation("GetAllManufacturers");
return _manufacturerStorageContract.GetList();
}
public ManufacturerDataModel GetManufacturerByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _manufacturerStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _manufacturerStorageContract.GetElementByName(data) ?? _manufacturerStorageContract.GetElementByOldName(data) ??
throw new ElementNotFoundException(data, _localizer);
}
public void InsertManufacturer(ManufacturerDataModel manufacturerDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(manufacturerDataModel));
ArgumentNullException.ThrowIfNull(manufacturerDataModel);
manufacturerDataModel.Validate(_localizer);
_manufacturerStorageContract.AddElement(manufacturerDataModel);
}
public void UpdateManufacturer(ManufacturerDataModel manufacturerDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(manufacturerDataModel));
ArgumentNullException.ThrowIfNull(manufacturerDataModel);
manufacturerDataModel.Validate(_localizer);
_manufacturerStorageContract.UpdElement(manufacturerDataModel);
}
public void DeleteManufacturer(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_manufacturerStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,96 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace CatHasPawsBusinessLogic.Implementations;
internal class PostBusinessLogicContract(IPostStorageContract postStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IPostBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IPostStorageContract _postStorageContract = postStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<PostDataModel> GetAllPosts()
{
_logger.LogInformation("GetAllPosts");
return _postStorageContract.GetList();
}
public List<PostDataModel> GetAllDataOfPost(string postId)
{
_logger.LogInformation("GetAllDataOfPost for {postId}", postId);
if (postId.IsEmpty())
{
throw new ArgumentNullException(nameof(postId));
}
if (!postId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "PostId"));
}
return _postStorageContract.GetPostWithHistory(postId);
}
public PostDataModel GetPostByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _postStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _postStorageContract.GetElementByName(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertPost(PostDataModel postDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(postDataModel));
ArgumentNullException.ThrowIfNull(postDataModel);
postDataModel.Validate(_localizer);
_postStorageContract.AddElement(postDataModel);
}
public void UpdatePost(PostDataModel postDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(postDataModel));
ArgumentNullException.ThrowIfNull(postDataModel);
postDataModel.Validate(_localizer);
_postStorageContract.UpdElement(postDataModel);
}
public void DeletePost(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_postStorageContract.DelElement(id);
}
public void RestorePost(string id)
{
_logger.LogInformation("Restore by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_postStorageContract.ResElement(id);
}
}

View File

@@ -0,0 +1,96 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace CatHasPawsBusinessLogic.Implementations;
internal class ProductBusinessLogicContract(IProductStorageContract productStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IProductBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IProductStorageContract _productStorageContract = productStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<ProductDataModel> GetAllProducts(bool onlyActive)
{
_logger.LogInformation("GetAllProducts params: {onlyActive}", onlyActive);
return _productStorageContract.GetList(onlyActive);
}
public List<ProductDataModel> GetAllProductsByManufacturer(string manufacturerId, bool onlyActive = true)
{
if (manufacturerId.IsEmpty())
{
throw new ArgumentNullException(nameof(manufacturerId));
}
if (!manufacturerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ManufacturerId"));
}
_logger.LogInformation("GetAllProducts params: {manufacturerId}, {onlyActive}", manufacturerId, onlyActive);
return _productStorageContract.GetList(onlyActive, manufacturerId);
}
public List<ProductHistoryDataModel> GetProductHistoryByProduct(string productId)
{
_logger.LogInformation("GetProductHistoryByProduct for {productId}", productId);
if (productId.IsEmpty())
{
throw new ArgumentNullException(nameof(productId));
}
if (!productId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ProductId"));
}
return _productStorageContract.GetHistoryByProductId(productId);
}
public ProductDataModel GetProductByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _productStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _productStorageContract.GetElementByName(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertProduct(ProductDataModel productDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(productDataModel));
ArgumentNullException.ThrowIfNull(productDataModel);
productDataModel.Validate(_localizer);
_productStorageContract.AddElement(productDataModel);
}
public void UpdateProduct(ProductDataModel productDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(productDataModel));
ArgumentNullException.ThrowIfNull(productDataModel);
productDataModel.Validate(_localizer);
_productStorageContract.UpdElement(productDataModel);
}
public void DeleteProduct(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_productStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,119 @@
using CatHasPawsBusinessLogic.OfficePackage;
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
namespace CatHasPawsBusinessLogic.Implementations;
internal class ReportContract : IReportContract
{
private readonly IProductStorageContract _productStorageContract;
private readonly ISaleStorageContract _saleStorageContract;
private readonly ISalaryStorageContract _salaryStorageContract;
private readonly BaseWordBuilder _baseWordBuilder;
private readonly BaseExcelBuilder _baseExcelBuilder;
private readonly BasePdfBuilder _basePdfBuilder;
private readonly IStringLocalizer<Messages> _localizer;
private readonly ILogger _logger;
internal readonly string[] _documentHeader;
internal readonly string[] _tableHeader;
public ReportContract(IProductStorageContract productStorageContract, ISaleStorageContract saleStorageContract, ISalaryStorageContract salaryStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, IStringLocalizer<Messages> localizer, ILogger logger)
{
_productStorageContract = productStorageContract;
_saleStorageContract = saleStorageContract;
_salaryStorageContract = salaryStorageContract;
_baseWordBuilder = baseWordBuilder;
_baseExcelBuilder = baseExcelBuilder;
_basePdfBuilder = basePdfBuilder;
_localizer = localizer;
_logger = logger;
_documentHeader = [_localizer["DocumentDocCaptionManufacturer"], _localizer["DocumentDocCaptionProduct"]];
_tableHeader = [_localizer["DocumentExcelCaptionDate"], _localizer["DocumentExcelCaptionSum"],
_localizer["DocumentExcelCaptionDiscount"], _localizer["DocumentExcelCaptionProduct"], _localizer["DocumentExcelCaptionCount"]];
}
public async Task<Stream> CreateDocumentProductsByManufacturerAsync(CancellationToken ct)
{
_logger.LogInformation("Create report ProductsByManufacturer");
var data = await GetDataByProductsAsync(ct);
return _baseWordBuilder
.AddHeader(_localizer["DocumentDocHeader"])
.AddParagraph(string.Format(_localizer["DocumentDocSubHeader"], DateTime.Now))
.AddTable([3000, 5000], [.. new List<string[]>() { _documentHeader }.Union([.. data.SelectMany(x => (new List<string[]>() { new string[] { x.ManufacturerName, "" } }).Union(x.Products.Select(y => new string[] { "", y })))])])
.Build();
}
public async Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report SalesByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataBySalesAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
return _baseExcelBuilder
.AddHeader(_localizer["DocumentExcelHeader"], 0, 5)
.AddParagraph(string.Format(_localizer["DocumentExcelSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()), 2)
.AddTable([10, 10, 10, 10, 10], [.. new List<string[]>() { _tableHeader }.Union(data.SelectMany(x => (new List<string[]>() { new string[] { x.SaleDate.ToShortDateString(), x.Sum.ToString("N2"), x.Discount.ToString("N2"), "", "" } }).Union(x.Products!.Select(y => new string[] { "", "", "", y.ProductName, y.Count.ToString("N2") })).ToArray())).Union([["Всего", data.Sum(x => x.Sum).ToString("N2"), data.Sum(x => x.Discount).ToString("N2"), "", ""]])])
.Build();
}
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(_localizer["DocumentPdfHeader"])
.AddParagraph(string.Format(_localizer["DocumentPdfSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()))
.AddPieChart("Начисления", [.. data.Select(x => (x.WorkerFIO, x.TotalSalary))])
.Build();
}
public Task<List<ManufacturerProductDataModel>> GetDataProductsByManufacturerAsync(CancellationToken ct)
{
_logger.LogInformation("Get data ProductsByManufacturer");
return GetDataByProductsAsync(ct);
}
public Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data SalesByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataBySalesAsync(dateStart, dateFinish, ct);
}
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<ManufacturerProductDataModel>> GetDataByProductsAsync(CancellationToken ct) => [.. (await _productStorageContract.GetListAsync(ct)).GroupBy(x => x.ManufacturerName).Select(x => new ManufacturerProductDataModel { ManufacturerName = x.Key, Products = [.. x.Select(y => y.ProductName)] })];
private async Task<List<SaleDataModel>> GetDataBySalesAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _saleStorageContract.GetListAsync(dateStart, dateFinish, ct)).OrderBy(x => x.SaleDate)];
}
private async Task<List<WorkerSalaryByPeriodDataModel>> GetDataBySalaryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _salaryStorageContract.GetListAsync(dateStart, dateFinish, ct)).GroupBy(x => x.WorkerId).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)];
}
}

View File

@@ -0,0 +1,124 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Infrastructure.PostConfigurations;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
namespace CatHasPawsBusinessLogic.Implementations;
internal class SalaryBusinessLogicContract(ISalaryStorageContract salaryStorageContract, ISaleStorageContract saleStorageContract, IPostStorageContract postStorageContract, IWorkerStorageContract workerStorageContract, IStringLocalizer<Messages> localizer, ILogger logger, IConfigurationSalary сonfiguration) : ISalaryBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly ISalaryStorageContract _salaryStorageContract = salaryStorageContract;
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
private readonly IPostStorageContract _postStorageContract = postStorageContract;
private readonly IWorkerStorageContract _workerStorageContract = workerStorageContract;
private readonly IConfigurationSalary _salaryConfiguration = сonfiguration;
private readonly IStringLocalizer<Messages> _localizer = localizer;
private readonly Lock _lockObject = new();
public List<SalaryDataModel> GetAllSalariesByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSalaries params: {fromDate}, {toDate}", fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _salaryStorageContract.GetList(fromDate, toDate);
}
public List<SalaryDataModel> GetAllSalariesByPeriodByWorker(DateTime fromDate, DateTime toDate, string workerId)
{
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (workerId.IsEmpty())
{
throw new ArgumentNullException(nameof(workerId));
}
if (!workerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
}
_logger.LogInformation("GetAllSalaries params: {fromDate}, {toDate}, {workerId}", fromDate, toDate, workerId);
return _salaryStorageContract.GetList(fromDate, toDate, workerId);
}
public void CalculateSalaryByMounth(DateTime date)
{
_logger.LogInformation("CalculateSalaryByMounth: {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 workers = _workerStorageContract.GetList();
foreach (var worker in workers)
{
var sales = _saleStorageContract.GetList(startDate, finishDate, workerId: worker.Id);
var post = _postStorageContract.GetElementById(worker.PostId) ?? throw new ElementNotFoundException(worker.PostId, _localizer);
var salary = post.ConfigurationModel switch
{
null => 0,
CashierPostConfiguration cpc => CalculateSalaryForCashier(sales, startDate, finishDate, cpc),
SupervisorPostConfiguration spc => CalculateSalaryForSupervisor(startDate, finishDate, spc),
PostConfiguration pc => pc.Rate,
};
_logger.LogDebug("The employee {workerId} was paid a salary of {salary}", worker.Id, salary);
_salaryStorageContract.AddElement(new SalaryDataModel(worker.Id, finishDate, salary));
}
}
private double CalculateSalaryForCashier(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, CashierPostConfiguration config)
{
var tasks = new List<Task>();
var calcPercent = 0.0;
for (var date = startDate; date < finishDate; date = date.AddDays(1))
{
tasks.Add(Task.Factory.StartNew((object? obj) =>
{
var dateInTask = (DateTime)obj!;
var salesInDay = sales.Where(x => x.SaleDate >= dateInTask && x.SaleDate <= dateInTask.AddDays(1)).ToArray();
if (salesInDay.Length > 0)
{
lock (_lockObject)
{
calcPercent += (salesInDay.Sum(x => x.Sum) / salesInDay.Length) * config.SalePercent;
}
}
}, date));
}
var calcBonusTask = Task.Run(() =>
{
return sales.Where(x => x.Sum > _salaryConfiguration.ExtraSaleSum).Sum(x => x.Sum) * config.BonusForExtraSales;
});
try
{
Task.WaitAll([Task.WhenAll(tasks), calcBonusTask]);
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
{
_logger.LogError(ex, "Error in the cashier payroll process");
}
return 0;
}
return config.Rate + calcPercent + calcBonusTask.Result;
}
private double CalculateSalaryForSupervisor(DateTime startDate, DateTime finishDate, SupervisorPostConfiguration config)
{
try
{
return config.Rate + config.PersonalCountTrendPremium * _workerStorageContract.GetWorkerTrend(startDate, finishDate);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in the supervisor payroll process");
return 0;
}
}
}

View File

@@ -0,0 +1,118 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace CatHasPawsBusinessLogic.Implementations;
internal class SaleBusinessLogicContract(ISaleStorageContract saleStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : ISaleBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<SaleDataModel> GetAllSalesByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {fromDate}, {toDate}", fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _saleStorageContract.GetList(fromDate, toDate);
}
public List<SaleDataModel> GetAllSalesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {workerId}, {fromDate}, {toDate}", workerId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (workerId.IsEmpty())
{
throw new ArgumentNullException(nameof(workerId));
}
if (!workerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
}
return _saleStorageContract.GetList(fromDate, toDate, workerId: workerId);
}
public List<SaleDataModel> GetAllSalesByBuyerByPeriod(string buyerId, DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {buyerId}, {fromDate}, {toDate}", buyerId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (buyerId.IsEmpty())
{
throw new ArgumentNullException(nameof(buyerId));
}
if (!buyerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "BuyerId"));
}
return _saleStorageContract.GetList(fromDate, toDate, buyerId: buyerId);
}
public List<SaleDataModel> GetAllSalesByProductByPeriod(string productId, DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {productId}, {fromDate}, {toDate}", productId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (productId.IsEmpty())
{
throw new ArgumentNullException(nameof(productId));
}
if (!productId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ProductId"));
}
return _saleStorageContract.GetList(fromDate, toDate, productId: productId);
}
public SaleDataModel GetSaleByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (!data.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
return _saleStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertSale(SaleDataModel saleDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(saleDataModel));
ArgumentNullException.ThrowIfNull(saleDataModel);
saleDataModel.Validate(_localizer);
_saleStorageContract.AddElement(saleDataModel);
}
public void CancelSale(string id)
{
_logger.LogInformation("Cancel by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_saleStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,102 @@
using CatHasPawsContratcs.BusinessLogicsContracts;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using System.Text.Json;
namespace CatHasPawsBusinessLogic.Implementations;
internal class WorkerBusinessLogicContract(IWorkerStorageContract workerStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IWorkerBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IWorkerStorageContract _workerStorageContract = workerStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<WorkerDataModel> GetAllWorkers(bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}", onlyActive);
return _workerStorageContract.GetList(onlyActive);
}
public List<WorkerDataModel> GetAllWorkersByPost(string postId, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {postId}, {onlyActive},", postId, onlyActive);
if (postId.IsEmpty())
{
throw new ArgumentNullException(nameof(postId));
}
if (!postId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "PostId"));
}
return _workerStorageContract.GetList(onlyActive, postId);
}
public List<WorkerDataModel> GetAllWorkersByBirthDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _workerStorageContract.GetList(onlyActive, fromBirthDate: fromDate, toBirthDate: toDate);
}
public List<WorkerDataModel> GetAllWorkersByEmploymentDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _workerStorageContract.GetList(onlyActive, fromEmploymentDate: fromDate, toEmploymentDate: toDate);
}
public WorkerDataModel GetWorkerByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _workerStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _workerStorageContract.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertWorker(WorkerDataModel workerDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(workerDataModel));
ArgumentNullException.ThrowIfNull(workerDataModel);
workerDataModel.Validate(_localizer);
_workerStorageContract.AddElement(workerDataModel);
}
public void UpdateWorker(WorkerDataModel workerDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(workerDataModel));
ArgumentNullException.ThrowIfNull(workerDataModel);
workerDataModel.Validate(_localizer);
_workerStorageContract.UpdElement(workerDataModel);
}
public void DeleteWorker(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_workerStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsBusinessLogic.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,12 @@
namespace CatHasPawsBusinessLogic.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,12 @@
namespace CatHasPawsBusinessLogic.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,85 @@
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Shapes.Charts;
using MigraDoc.Rendering;
using System.Text;
namespace CatHasPawsBusinessLogic.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 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,303 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
namespace CatHasPawsBusinessLogic.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,94 @@
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
namespace CatHasPawsBusinessLogic.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

@@ -0,0 +1,17 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IBuyerAdapter
{
BuyerOperationResponse GetList();
BuyerOperationResponse GetElement(string data);
BuyerOperationResponse RegisterBuyer(BuyerBindingModel buyerModel);
BuyerOperationResponse ChangeBuyerInfo(BuyerBindingModel buyerModel);
BuyerOperationResponse RemoveBuyer(string id);
}

View File

@@ -0,0 +1,17 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IManufacturerAdapter
{
ManufacturerOperationResponse GetList();
ManufacturerOperationResponse GetElement(string data);
ManufacturerOperationResponse RegisterManufacturer(ManufacturerBindingModel manufacturerModel);
ManufacturerOperationResponse ChangeManufacturerInfo(ManufacturerBindingModel manufacturerModel);
ManufacturerOperationResponse RemoveManufacturer(string id);
}

View File

@@ -0,0 +1,21 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IPostAdapter
{
PostOperationResponse GetList();
PostOperationResponse GetHistory(string id);
PostOperationResponse GetElement(string data);
PostOperationResponse RegisterPost(PostBindingModel postModel);
PostOperationResponse ChangePostInfo(PostBindingModel postModel);
PostOperationResponse RemovePost(string id);
PostOperationResponse RestorePost(string id);
}

View File

@@ -0,0 +1,21 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IProductAdapter
{
ProductOperationResponse GetList(bool includeDeleted);
ProductOperationResponse GetManufacturerList(string id, bool includeDeleted);
ProductOperationResponse GetHistory(string id);
ProductOperationResponse GetElement(string data);
ProductOperationResponse RegisterProduct(ProductBindingModel productModel);
ProductOperationResponse ChangeProductInfo(ProductBindingModel productModel);
ProductOperationResponse RemoveProduct(string id);
}

View File

@@ -0,0 +1,18 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataProductsByManufacturerAsync(CancellationToken ct);
Task<ReportOperationResponse> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentProductsByManufacturerAsync(CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,21 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface ISaleAdapter
{
SaleOperationResponse GetList(DateTime fromDate, DateTime toDate);
SaleOperationResponse GetWorkerList(string id, DateTime fromDate, DateTime toDate);
SaleOperationResponse GetBuyerList(string id, DateTime fromDate, DateTime toDate);
SaleOperationResponse GetProductList(string id, DateTime fromDate, DateTime toDate);
SaleOperationResponse GetElement(string id);
SaleOperationResponse MakeSale(SaleBindingModel saleModel);
SaleOperationResponse CancelSale(string id);
}

View File

@@ -0,0 +1,23 @@
using CatHasPawsContratcs.AdapterContracts.OperationResponses;
using CatHasPawsContratcs.BindingModels;
namespace CatHasPawsContratcs.AdapterContracts;
public interface IWorkerAdapter
{
WorkerOperationResponse GetList(bool includeDeleted);
WorkerOperationResponse GetPostList(string id, bool includeDeleted);
WorkerOperationResponse GetListByBirthDate(DateTime fromDate, DateTime toDate, bool includeDeleted);
WorkerOperationResponse GetListByEmploymentDate(DateTime fromDate, DateTime toDate, bool includeDeleted);
WorkerOperationResponse GetElement(string data);
WorkerOperationResponse RegisterWorker(WorkerBindingModel workerModel);
WorkerOperationResponse ChangeWorkerInfo(WorkerBindingModel workerModel);
WorkerOperationResponse RemoveWorker(string id);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class BuyerOperationResponse : OperationResponse
{
public static BuyerOperationResponse OK(List<BuyerViewModel> data) => OK<BuyerOperationResponse, List<BuyerViewModel>>(data);
public static BuyerOperationResponse OK(BuyerViewModel data) => OK<BuyerOperationResponse, BuyerViewModel>(data);
public static BuyerOperationResponse NoContent() => NoContent<BuyerOperationResponse>();
public static BuyerOperationResponse BadRequest(string message) => BadRequest<BuyerOperationResponse>(message);
public static BuyerOperationResponse NotFound(string message) => NotFound<BuyerOperationResponse>(message);
public static BuyerOperationResponse InternalServerError(string message) => InternalServerError<BuyerOperationResponse>(message);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class ManufacturerOperationResponse : OperationResponse
{
public static ManufacturerOperationResponse OK(List<ManufacturerViewModel> data) => OK<ManufacturerOperationResponse, List<ManufacturerViewModel>>(data);
public static ManufacturerOperationResponse OK(ManufacturerViewModel data) => OK<ManufacturerOperationResponse, ManufacturerViewModel>(data);
public static ManufacturerOperationResponse NoContent() => NoContent<ManufacturerOperationResponse>();
public static ManufacturerOperationResponse NotFound(string message) => NotFound<ManufacturerOperationResponse>(message);
public static ManufacturerOperationResponse BadRequest(string message) => BadRequest<ManufacturerOperationResponse>(message);
public static ManufacturerOperationResponse InternalServerError(string message) => InternalServerError<ManufacturerOperationResponse>(message);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class PostOperationResponse : OperationResponse
{
public static PostOperationResponse OK(List<PostViewModel> data) => OK<PostOperationResponse, List<PostViewModel>>(data);
public static PostOperationResponse OK(PostViewModel data) => OK<PostOperationResponse, PostViewModel>(data);
public static PostOperationResponse NoContent() => NoContent<PostOperationResponse>();
public static PostOperationResponse NotFound(string message) => NotFound<PostOperationResponse>(message);
public static PostOperationResponse BadRequest(string message) => BadRequest<PostOperationResponse>(message);
public static PostOperationResponse InternalServerError(string message) => InternalServerError<PostOperationResponse>(message);
}

View File

@@ -0,0 +1,21 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class ProductOperationResponse : OperationResponse
{
public static ProductOperationResponse OK(List<ProductViewModel> data) => OK<ProductOperationResponse, List<ProductViewModel>>(data);
public static ProductOperationResponse OK(List<ProductHistoryViewModel> data) => OK<ProductOperationResponse, List<ProductHistoryViewModel>>(data);
public static ProductOperationResponse OK(ProductViewModel data) => OK<ProductOperationResponse, ProductViewModel>(data);
public static ProductOperationResponse NoContent() => NoContent<ProductOperationResponse>();
public static ProductOperationResponse NotFound(string message) => NotFound<ProductOperationResponse>(message);
public static ProductOperationResponse BadRequest(string message) => BadRequest<ProductOperationResponse>(message);
public static ProductOperationResponse InternalServerError(string message) => InternalServerError<ProductOperationResponse>(message);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ManufacturerProductViewModel> data) => OK<ReportOperationResponse, List<ManufacturerProductViewModel>>(data);
public static ReportOperationResponse OK(List<SaleViewModel> data) => OK<ReportOperationResponse, List<SaleViewModel>>(data);
public static ReportOperationResponse OK(List<WorkerSalaryByPeriodViewModel> data) => OK<ReportOperationResponse, List<WorkerSalaryByPeriodViewModel>>(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

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class SaleOperationResponse : OperationResponse
{
public static SaleOperationResponse OK(List<SaleViewModel> data) => OK<SaleOperationResponse, List<SaleViewModel>>(data);
public static SaleOperationResponse OK(SaleViewModel data) => OK<SaleOperationResponse, SaleViewModel>(data);
public static SaleOperationResponse NoContent() => NoContent<SaleOperationResponse>();
public static SaleOperationResponse NotFound(string message) => NotFound<SaleOperationResponse>(message);
public static SaleOperationResponse BadRequest(string message) => BadRequest<SaleOperationResponse>(message);
public static SaleOperationResponse InternalServerError(string message) => InternalServerError<SaleOperationResponse>(message);
}

View File

@@ -0,0 +1,19 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.ViewModels;
namespace CatHasPawsContratcs.AdapterContracts.OperationResponses;
public class WorkerOperationResponse : OperationResponse
{
public static WorkerOperationResponse OK(List<WorkerViewModel> data) => OK<WorkerOperationResponse, List<WorkerViewModel>>(data);
public static WorkerOperationResponse OK(WorkerViewModel data) => OK<WorkerOperationResponse, WorkerViewModel>(data);
public static WorkerOperationResponse NoContent() => NoContent<WorkerOperationResponse>();
public static WorkerOperationResponse NotFound(string message) => NotFound<WorkerOperationResponse>(message);
public static WorkerOperationResponse BadRequest(string message) => BadRequest<WorkerOperationResponse>(message);
public static WorkerOperationResponse InternalServerError(string message) => InternalServerError<WorkerOperationResponse>(message);
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.BindingModels;
public class BuyerBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? PhoneNumber { get; set; }
public double DiscountSize { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.BindingModels;
public class ManufacturerBindingModel
{
public string? Id { get; set; }
public string? ManufacturerName { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace CatHasPawsContratcs.BindingModels;
public class PostBindingModel
{
public string? Id { get; set; }
public string? PostId => Id;
public string? PostName { get; set; }
public string? PostType { get; set; }
public string? ConfigurationJson { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace CatHasPawsContratcs.BindingModels;
public class ProductBindingModel
{
public string? Id { get; set; }
public string? ProductName { get; set; }
public string? ProductType { get; set; }
public string? ManufacturerId { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace CatHasPawsContratcs.BindingModels;
public class SaleBindingModel
{
public string? Id { get; set; }
public string? WorkerId { get; set; }
public string? BuyerId { get; set; }
public int DiscountType { get; set; }
public List<SaleProductBindingModel>? Products { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.BindingModels;
public class SaleProductBindingModel
{
public string? SaleId { get; set; }
public string? ProductId { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace CatHasPawsContratcs.BindingModels;
public class WorkerBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? PostId { get; set; }
public DateTime BirthDate { get; set; }
public DateTime EmploymentDate { get; set; }
}

View File

@@ -0,0 +1,16 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IBuyerBusinessLogicContract
{
List<BuyerDataModel> GetAllBuyers();
BuyerDataModel GetBuyerByData(string data);
void InsertBuyer(BuyerDataModel buyerDataModel);
void UpdateBuyer(BuyerDataModel buyerDataModel);
void DeleteBuyer(string id);
}

View File

@@ -0,0 +1,16 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IManufacturerBusinessLogicContract
{
List<ManufacturerDataModel> GetAllManufacturers();
ManufacturerDataModel GetManufacturerByData(string data);
void InsertManufacturer(ManufacturerDataModel manufacturerDataModel);
void UpdateManufacturer(ManufacturerDataModel manufacturerDataModel);
void DeleteManufacturer(string id);
}

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IPostBusinessLogicContract
{
List<PostDataModel> GetAllPosts();
List<PostDataModel> GetAllDataOfPost(string postId);
PostDataModel GetPostByData(string data);
void InsertPost(PostDataModel postDataModel);
void UpdatePost(PostDataModel postDataModel);
void DeletePost(string id);
void RestorePost(string id);
}

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IProductBusinessLogicContract
{
List<ProductDataModel> GetAllProducts(bool onlyActive = true);
List<ProductDataModel> GetAllProductsByManufacturer(string manufacturerId, bool onlyActive = true);
List<ProductHistoryDataModel> GetProductHistoryByProduct(string productId);
ProductDataModel GetProductByData(string data);
void InsertProduct(ProductDataModel productDataModel);
void UpdateProduct(ProductDataModel productDataModel);
void DeleteProduct(string id);
}

View File

@@ -0,0 +1,18 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IReportContract
{
Task<List<ManufacturerProductDataModel>> GetDataProductsByManufacturerAsync(CancellationToken ct);
Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<WorkerSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentProductsByManufacturerAsync(CancellationToken ct);
Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,12 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface ISalaryBusinessLogicContract
{
List<SalaryDataModel> GetAllSalariesByPeriod(DateTime fromDate, DateTime toDate);
List<SalaryDataModel> GetAllSalariesByPeriodByWorker(DateTime fromDate, DateTime toDate, string workerId);
void CalculateSalaryByMounth(DateTime date);
}

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface ISaleBusinessLogicContract
{
List<SaleDataModel> GetAllSalesByPeriod(DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByBuyerByPeriod(string buyerId, DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByProductByPeriod(string productId, DateTime fromDate, DateTime toDate);
SaleDataModel GetSaleByData(string data);
void InsertSale(SaleDataModel saleDataModel);
void CancelSale(string id);
}

View File

@@ -0,0 +1,22 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.BusinessLogicsContracts;
internal interface IWorkerBusinessLogicContract
{
List<WorkerDataModel> GetAllWorkers(bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersByPost(string postId, bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersByBirthDate(DateTime fromDate, DateTime toDate, bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersByEmploymentDate(DateTime fromDate, DateTime toDate, bool onlyActive = true);
WorkerDataModel GetWorkerByData(string data);
void InsertWorker(WorkerDataModel workerDataModel);
void UpdateWorker(WorkerDataModel workerDataModel);
void DeleteWorker(string id);
}

View File

@@ -6,4 +6,33 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Messages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Messages.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Messages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsBusinessLogic" />
<InternalsVisibleTo Include="CatHasPawsDatabase" />
<InternalsVisibleTo Include="CatHasPawsWebApi" />
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -1,11 +1,13 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
using System.Text.RegularExpressions;
namespace CatHasPawsContratcs.DataModels;
public class BuyerDataModel(string id, string fio, string phoneNumber, double discountSize) : IValidation
internal class BuyerDataModel(string id, string fio, string phoneNumber, double discountSize) : IValidation
{
public string Id { get; private set; } = id;
@@ -15,21 +17,23 @@ public class BuyerDataModel(string id, string fio, string phoneNumber, double di
public double DiscountSize { get; private set; } = discountSize;
public void Validate()
public BuyerDataModel() : this(string.Empty, string.Empty, string.Empty, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (FIO.IsEmpty())
throw new ValidationException("Field FIO is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "FIO"));
if (PhoneNumber.IsEmpty())
throw new ValidationException("Field PhoneNumber is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PhoneNumber"));
if (!Regex.IsMatch(PhoneNumber, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
throw new ValidationException("Field PhoneNumber is not a phone number");
throw new ValidationException(localizer["ValidationExceptionMessageIncorrectPhoneNumber"]);
}
}

View File

@@ -1,10 +1,12 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class ManufacturerDataModel(string id, string manufacturerName, string? prevManufacturerName, string? prevPrevManufacturerName) : IValidation
internal class ManufacturerDataModel(string id, string manufacturerName, string? prevManufacturerName, string? prevPrevManufacturerName) : IValidation
{
public string Id { get; private set; } = id;
@@ -14,15 +16,17 @@ public class ManufacturerDataModel(string id, string manufacturerName, string? p
public string? PrevPrevManufacturerName { get; private set; } = prevPrevManufacturerName;
public void Validate()
public ManufacturerDataModel(string id, string manufacturerName) : this(id, manufacturerName, null, null) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (ManufacturerName.IsEmpty())
throw new ValidationException("Field ManufacturerName is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ManufacturerName"));
}
}

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.DataModels;
public class ManufacturerProductDataModel
{
public required string ManufacturerName { get; set; }
public required List<string> Products { get; set; }
}

View File

@@ -2,46 +2,63 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Infrastructure.PostConfigurations;
using CatHasPawsContratcs.Mapper;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CatHasPawsContratcs.DataModels;
public class PostDataModel(string id, string postId, string postName, PostType postType, double salary, bool isActual, DateTime changeDate) : IValidation
internal class PostDataModel(string postId, string postName, PostType postType, PostConfiguration configuration) : IValidation
{
public string Id { get; private set; } = id;
public string PostId { get; private set; } = postId;
public string Id { get; private set; } = postId;
public string PostName { get; private set; } = postName;
public PostType PostType { get; private set; } = postType;
public double Salary { get; private set; } = salary;
[AlternativeName("ConfigurationJson")]
[AlternativeName("Configuration")]
[PostProcessing(MappingCallMethodName = "ParseJson")]
public PostConfiguration ConfigurationModel { get; private set; } = configuration;
public bool IsActual { get; private set; } = isActual;
public PostDataModel() : this(string.Empty, string.Empty, PostType.None, null) { }
public DateTime ChangeDate { get; private set; } = changeDate;
public void Validate()
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
if (PostId.IsEmpty())
throw new ValidationException("Field PostId is empty");
if (!PostId.IsGuid())
throw new ValidationException("The value in the field PostId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (PostName.IsEmpty())
throw new ValidationException("Field PostName is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostName"));
if (PostType == PostType.None)
throw new ValidationException("Field PostType is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostType"));
if (Salary <= 0)
throw new ValidationException("Field Salary is empty");
if (ConfigurationModel is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "ConfigurationModel"));
if (ConfigurationModel!.Rate <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Rate"));
}
private PostConfiguration? ParseJson(string json)
{
var obj = JToken.Parse(json);
if (obj is not null)
{
return obj.Value<string>("Type") switch
{
nameof(CashierPostConfiguration) => JsonConvert.DeserializeObject<CashierPostConfiguration>(json)!,
nameof(SupervisorPostConfiguration) => JsonConvert.DeserializeObject<SupervisorPostConfiguration>(json)!,
_ => JsonConvert.DeserializeObject<PostConfiguration>(json)!,
};
}
return null;
}
}

View File

@@ -2,11 +2,15 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price, bool isDeleted) : IValidation
internal class ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price, bool isDeleted) : IValidation
{
private readonly ManufacturerDataModel? _manufacturer;
public string Id { get; private set; } = id;
public string ProductName { get; private set; } = productName;
@@ -19,27 +23,36 @@ public class ProductDataModel(string id, string productName, ProductType product
public bool IsDeleted { get; private set; } = isDeleted;
public void Validate()
public string ManufacturerName => _manufacturer?.ManufacturerName ?? string.Empty;
public ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price, bool isDeleted, ManufacturerDataModel manufacturer) : this(id, productName, productType, manufacturerId, price, isDeleted)
{
_manufacturer = manufacturer;
}
public ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price) : this(id, productName, productType, manufacturerId, price, false) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (ProductName.IsEmpty())
throw new ValidationException("Field ProductName is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductName"));
if (ProductType == ProductType.None)
throw new ValidationException("Field ProductType is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductType"));
if (ManufacturerId.IsEmpty())
throw new ValidationException("Field ManufacturerId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ManufacturerId"));
if (!ManufacturerId.IsGuid())
throw new ValidationException("The value in the field ManufacturerId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ManufacturerId"));
if (Price <= 0)
throw new ValidationException("Field Price is less than or equal to 0");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

View File

@@ -1,26 +1,38 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class ProductHistoryDataModel(string productId, double oldPrice) : IValidation
internal class ProductHistoryDataModel(string productId, double oldPrice) : IValidation
{
private readonly ProductDataModel? _product;
public string ProductId { get; private set; } = productId;
public double OldPrice { get; private set; } = oldPrice;
public DateTime ChangeDate { get; private set; } = DateTime.UtcNow;
public void Validate()
public string ProductName => _product?.ProductName ?? string.Empty;
public ProductHistoryDataModel(string productId, double oldPrice, DateTime changeDate, ProductDataModel product) : this(productId, oldPrice)
{
ChangeDate = changeDate;
_product = product;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (ProductId.IsEmpty())
throw new ValidationException("Field ProductId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductId"));
if (!ProductId.IsGuid())
throw new ValidationException("The value in the field ProductId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ProductId"));
if (OldPrice <= 0)
throw new ValidationException("Field OldPrice is less than or equal to 0");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "OldPrice"));
}
}

View File

@@ -1,26 +1,28 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class SalaryDataModel(string workerId, DateTime salaryDate, double workerSalary) : IValidation
internal class SalaryDataModel(string workerId, DateTime salaryDate, double workerSalary) : IValidation
{
public string WorkerId { get; private set; } = workerId;
public DateTime SalaryDate { get; private set; } = salaryDate;
public DateTime SalaryDate { get; private set; } = salaryDate.ToUniversalTime();
public double Salary { get; private set; } = workerSalary;
public void Validate()
public void Validate(IStringLocalizer<Messages> localizer)
{
if (WorkerId.IsEmpty())
throw new ValidationException("Field WorkerId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkerId"));
if (!WorkerId.IsGuid())
throw new ValidationException("The value in the field WorkerId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
if (Salary <= 0)
throw new ValidationException("Field Salary is less than or equal to 0");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Salary"));
}
}

View File

@@ -2,50 +2,108 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class SaleDataModel(string id, string workerId, string? buyerId, double sum, DiscountType discountType, double discount, bool isCancel, List<SaleProductDataModel> products) : IValidation
internal class SaleDataModel : IValidation
{
public string Id { get; private set; } = id;
private readonly BuyerDataModel? _buyer;
public string WorkerId { get; private set; } = workerId;
private readonly WorkerDataModel? _worker;
public string? BuyerId { get; private set; } = buyerId;
public string Id { get; private set; }
public string WorkerId { get; private set; }
public string? BuyerId { get; private set; }
public DateTime SaleDate { get; private set; } = DateTime.UtcNow;
public double Sum { get; private set; } = sum;
public double Sum { get; private set; }
public DiscountType DiscountType { get; private set; } = discountType;
public DiscountType DiscountType { get; private set; }
public double Discount { get; private set; } = discount;
public double Discount { get; private set; }
public bool IsCancel { get; private set; } = isCancel;
public bool IsCancel { get; private set; }
public List<SaleProductDataModel> Products { get; private set; } = products;
public List<SaleProductDataModel>? Products { get; private set; }
public void Validate()
public string BuyerFIO => _buyer?.FIO ?? string.Empty;
public string WorkerFIO => _worker?.FIO ?? string.Empty;
public SaleDataModel(string id, string workerId, string? buyerId, DiscountType discountType, bool isCancel, List<SaleProductDataModel> saleProducts)
{
Id = id;
WorkerId = workerId;
BuyerId = buyerId;
DiscountType = discountType;
IsCancel = isCancel;
Products = saleProducts;
var percent = 0.0;
foreach (DiscountType elem in Enum.GetValues<DiscountType>())
{
if ((elem & discountType) != 0)
{
switch (elem)
{
case DiscountType.None:
break;
case DiscountType.OnSale:
percent += 0.1;
break;
case DiscountType.RegularCustomer:
percent += 0.5;
break;
case DiscountType.Certificate:
percent += 0.3;
break;
}
}
}
Sum = Products?.Sum(x => x.Price * x.Count) ?? 0;
Discount = Sum * percent;
}
public SaleDataModel(string id, string workerId, string? buyerId, double sum, DiscountType discountType, double discount, bool isCancel, List<SaleProductDataModel> saleProducts, WorkerDataModel worker, BuyerDataModel? buyer) : this(id, workerId, buyerId, discountType, isCancel, saleProducts)
{
Sum = sum;
Discount = discount;
_worker = worker;
_buyer = buyer;
}
public SaleDataModel(string id, string workerId, string? buyerId, int discountType, List<SaleProductDataModel> products) : this(id, workerId, buyerId, (DiscountType)discountType, false, products) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (WorkerId.IsEmpty())
throw new ValidationException("Field WorkerId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkerId"));
if (!WorkerId.IsGuid())
throw new ValidationException("The value in the field WorkerId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
if (!BuyerId?.IsGuid() ?? !BuyerId?.IsEmpty() ?? false)
throw new ValidationException("The value in the field BuyerId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "BuyerId"));
if (Sum <= 0)
throw new ValidationException("Field Sum is less than or equal to 0");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Sum"));
if ((Products?.Count ?? 0) == 0)
throw new ValidationException("The sale must include products");
if (Products is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "Products"));
if (Products.Count == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoProductsInSale"]);
Products.ForEach(x => x.Validate(localizer));
}
}

View File

@@ -1,32 +1,48 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class SaleProductDataModel(string saleId, string productId, int count) : IValidation
internal class SaleProductDataModel(string saleId, string productId, int count, double price) : IValidation
{
private readonly ProductDataModel? _product;
public string SaleId { get; private set; } = saleId;
public string ProductId { get; private set; } = productId;
public int Count { get; private set; } = count;
public void Validate()
public double Price { get; private set; } = price;
public string ProductName => _product?.ProductName ?? string.Empty;
public SaleProductDataModel(string saleId, string productId, int count, double price, ProductDataModel product) : this(saleId, productId, count, price)
{
_product = product;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (SaleId.IsEmpty())
throw new ValidationException("Field SaleId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SaleId"));
if (!SaleId.IsGuid())
throw new ValidationException("The value in the field SaleId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "SaleId"));
if (ProductId.IsEmpty())
throw new ValidationException("Field ProductId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IProductIdd"));
if (!ProductId.IsGuid())
throw new ValidationException("The value in the field ProductId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ProductId"));
if (Count <= 0)
throw new ValidationException("Field Count is less than or equal to 0");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if (Price <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

View File

@@ -1,47 +1,62 @@
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Extensions;
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.DataModels;
public class WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted) : IValidation
internal class WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted) : IValidation
{
private readonly PostDataModel? _post;
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string PostId { get; private set; } = postId;
public DateTime BirthDate { get; private set; } = birthDate;
public DateTime BirthDate { get; private set; } = birthDate.ToUniversalTime();
public DateTime EmploymentDate { get; private set; } = employmentDate;
public DateTime EmploymentDate { get; private set; } = employmentDate.ToUniversalTime();
public bool IsDeleted { get; private set; } = isDeleted;
public void Validate()
public string PostName => _post?.PostName ?? string.Empty;
public WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted, PostDataModel post) : this(id, fio, postId, birthDate, employmentDate, isDeleted)
{
_post = post;
}
public WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate) : this(id, fio, postId, birthDate, employmentDate, false) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException("Field Id is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException("The value in the field Id is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (FIO.IsEmpty())
throw new ValidationException("Field FIO is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "FIO"));
if (PostId.IsEmpty())
throw new ValidationException("Field PostId is empty");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostId"));
if (!PostId.IsGuid())
throw new ValidationException("The value in the field PostId is not a unique identifier");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "PostId"));
if (BirthDate.Date > DateTime.Now.AddYears(-16).Date)
throw new ValidationException($"Minors cannot be hired (BirthDate = {BirthDate.ToShortDateString()})");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsBirthDate"], BirthDate.ToShortDateString()));
if (EmploymentDate.Date < BirthDate.Date)
throw new ValidationException("The date of employment cannot be less than the date of birth");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmploymentDateAndBirthDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
if ((EmploymentDate - BirthDate).TotalDays / 365 < 16) // EmploymentDate.Year - BirthDate.Year
throw new ValidationException($"Minors cannot be hired (EmploymentDate - {EmploymentDate.ToShortDateString()}, BirthDate - {BirthDate.ToShortDateString()})");
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsEmploymentDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
}
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.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

@@ -0,0 +1,8 @@
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.Exceptions;
internal class ElementDeletedException(string id, IStringLocalizer<Messages> localizer) :
Exception(string.Format(localizer["ElementDeletedExceptionMessage"], id))
{ }

View File

@@ -0,0 +1,12 @@
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.Exceptions;
internal class ElementExistsException(string paramName, string paramValue, IStringLocalizer<Messages> localizer) :
Exception(string.Format(localizer["ElementExistsExceptionMessage"], paramValue, paramName))
{
public string ParamName { get; private set; } = paramName;
public string ParamValue { get; private set; } = paramValue;
}

View File

@@ -0,0 +1,10 @@
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.Exceptions;
internal class ElementNotFoundException(string value, IStringLocalizer<Messages> localizer) :
Exception(string.Format(localizer["ElementNotFoundExceptionMessage"], value))
{
public string Value { get; private set; } = value;
}

View File

@@ -0,0 +1,8 @@
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.Exceptions;
internal class IncorrectDatesException(DateTime start, DateTime end, IStringLocalizer<Messages> localizer) :
Exception(string.Format(localizer["IncorrectDatesExceptionMessage"], start.ToShortDateString(), end.ToShortDateString()))
{ }

View File

@@ -0,0 +1,8 @@
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
namespace CatHasPawsContratcs.Exceptions;
internal class StorageException(Exception ex, IStringLocalizer<Messages> localizer) :
Exception(string.Format(localizer["StorageExceptionMessage"], ex.Message), ex)
{ }

View File

@@ -1,5 +1,4 @@
namespace CatHasPawsContratcs.Exceptions;
public class ValidationException(string message) : Exception(message)
{
}
{ }

View File

@@ -0,0 +1,9 @@
namespace CatHasPawsContratcs.Extensions;
public static class DateTimeExtensions
{
public static bool IsDateNotOlder(this DateTime date, DateTime olderDate)
{
return date >= olderDate;
}
}

View File

@@ -0,0 +1,6 @@
namespace CatHasPawsContratcs.Infrastructure;
public interface IConfigurationDatabase
{
string ConnectionString { get; }
}

View File

@@ -0,0 +1,6 @@
namespace CatHasPawsContratcs.Infrastructure;
public interface IConfigurationSalary
{
double ExtraSaleSum { get; }
}

View File

@@ -1,6 +1,9 @@
namespace CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Resources;
using Microsoft.Extensions.Localization;
public interface IValidation
namespace CatHasPawsContratcs.Infrastructure;
internal interface IValidation
{
void Validate();
void Validate(IStringLocalizer<Messages> localizer);
}

View File

@@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace CatHasPawsContratcs.Infrastructure;
public class OperationResponse
{
protected HttpStatusCode StatusCode { get; set; }
protected object? Result { get; set; }
protected string? FileName { get; set; }
public IActionResult GetResponse(HttpRequest request, HttpResponse response)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(response);
response.StatusCode = (int)StatusCode;
if (Result is null)
{
return new StatusCodeResult((int)StatusCode);
}
if (Result is Stream stream)
{
return new FileStreamResult(stream, "application/octet-stream")
{
FileDownloadName = FileName
};
}
return new ObjectResult(Result);
}
protected static TResult OK<TResult, TData>(TData data) 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 };
protected static TResult BadRequest<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.BadRequest, Result = errorMessage };
protected static TResult NotFound<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.NotFound, Result = errorMessage };
protected static TResult InternalServerError<TResult>(string? errorMessage = null) where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.InternalServerError, Result = errorMessage };
}

View File

@@ -0,0 +1,10 @@
namespace CatHasPawsContratcs.Infrastructure.PostConfigurations;
public class CashierPostConfiguration : PostConfiguration
{
public override string Type => nameof(CashierPostConfiguration);
public double SalePercent { get; set; }
public double BonusForExtraSales { get; set; }
}

View File

@@ -0,0 +1,12 @@
using System.Globalization;
namespace CatHasPawsContratcs.Infrastructure.PostConfigurations;
public class PostConfiguration
{
public virtual string Type => nameof(PostConfiguration);
public double Rate { get; set; }
public string CultureName { get; set; } = CultureInfo.CurrentCulture.Name;
}

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.Infrastructure.PostConfigurations;
public class SupervisorPostConfiguration : PostConfiguration
{
public override string Type => nameof(SupervisorPostConfiguration);
public double PersonalCountTrendPremium { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace CatHasPawsContratcs.Mapper;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
class AlternativeNameAttribute(string alternativeName) : Attribute
{
public string AlternativeName { get; set; } = alternativeName;
}

View File

@@ -0,0 +1,159 @@
using System.Collections;
using System.Reflection;
namespace CatHasPawsContratcs.Mapper;
internal static class CustomMapper
{
public static To MapObject<To>(object obj, To newObject)
{
ArgumentNullException.ThrowIfNull(obj);
ArgumentNullException.ThrowIfNull(newObject);
var typeFrom = obj.GetType();
var typeTo = newObject.GetType();
var propertiesFrom = typeFrom.GetProperties().Where(x => x.CanRead).ToArray();
foreach (var property in typeTo.GetProperties().Where(x => x.CanWrite))
{
//ignore
var propertyFrom = TryGetPropertyFrom(property, propertiesFrom);
if (propertyFrom is null)
{
FindAndMapDefaultValue(property, newObject);
continue;
}
var fromValue = propertyFrom.GetValue(obj);
var postProcessingAttribute = property.GetCustomAttribute<PostProcessingAttribute>();
if (postProcessingAttribute is not null)
{
var value = PostProcessing(fromValue, postProcessingAttribute, newObject);
if (value is not null)
{
property.SetValue(newObject, value);
}
continue;
}
if (propertyFrom.PropertyType.IsGenericType && propertyFrom.PropertyType.Name.StartsWith("List") && fromValue is not null)
{
fromValue = MapListOfObjects(property, fromValue);
}
if (fromValue is not null)
{
// propertyFrom is enum && property is not enum ToString
// property is enum Enum.TryParse
property.SetValue(newObject, fromValue);
}
}
// fields
var classPostProcessing = typeTo.GetCustomAttribute<PostProcessingAttribute>();
if (classPostProcessing is not null && classPostProcessing.MappingCallMethodName is not null)
{
var methodInfo = typeTo.GetMethod(classPostProcessing.MappingCallMethodName, BindingFlags.NonPublic | BindingFlags.Instance);
methodInfo?.Invoke(newObject, []);
}
return newObject;
}
public static To MapObject<To>(object obj) => MapObject(obj, Activator.CreateInstance<To>()!);
public static To? MapObjectWithNull<To>(object? obj) => obj is null ? default : MapObject(obj, Activator.CreateInstance<To>());
private static PropertyInfo? TryGetPropertyFrom(PropertyInfo propertyTo, PropertyInfo[] propertiesFrom)
{
var customAttribute = propertyTo.GetCustomAttributes<AlternativeNameAttribute>()?
.ToArray()
.FirstOrDefault(x => propertiesFrom.Any(y => y.Name == x.AlternativeName));
if (customAttribute is not null)
{
return propertiesFrom.FirstOrDefault(x => x.Name == customAttribute.AlternativeName);
}
return propertiesFrom.FirstOrDefault(x => x.Name == propertyTo.Name);
}
private static object? PostProcessing<T>(object? value, PostProcessingAttribute postProcessingAttribute, T newObject)
{
if (value is null || newObject is null)
{
return null;
}
if (!string.IsNullOrEmpty(postProcessingAttribute.MappingCallMethodName))
{
var methodInfo =
newObject.GetType().GetMethod(postProcessingAttribute.MappingCallMethodName, BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo is not null)
{
return methodInfo.Invoke(newObject, [value]);
}
}
else if (postProcessingAttribute.ActionType != PostProcessingType.None)
{
switch (postProcessingAttribute.ActionType)
{
case PostProcessingType.ToUniversalTime:
return ToUniversalTime(value);
case PostProcessingType.ToLocalTime:
return ToLocalTime(value);
}
}
return null;
}
private static object? ToLocalTime(object? obj)
{
if (obj is DateTime date)
return date.ToLocalTime();
return obj;
}
private static object? ToUniversalTime(object? obj)
{
if (obj is DateTime date)
return date.ToUniversalTime();
return obj;
}
private static void FindAndMapDefaultValue<T>(PropertyInfo property, T newObject)
{
var defaultValueAttribute = property.GetCustomAttribute<DefaultValueAttribute>();
if (defaultValueAttribute is null)
{
return;
}
if (defaultValueAttribute.DefaultValue is not null)
{
property.SetValue(newObject, defaultValueAttribute.DefaultValue);
return;
}
var value = defaultValueAttribute.FuncName switch
{
"UtcNow" => DateTime.UtcNow,
_ => (object?)null,
};
if (value is not null)
{
property.SetValue(newObject, value);
}
}
private static object? MapListOfObjects(PropertyInfo propertyTo, object list)
{
var listResult = Activator.CreateInstance(propertyTo.PropertyType);
foreach (var elem in (IEnumerable)list)
{
var newElem = MapObject(elem, Activator.CreateInstance(propertyTo.PropertyType.GenericTypeArguments[0])!);
if (newElem is not null)
{
propertyTo.PropertyType.GetMethod("Add")!.Invoke(listResult, [newElem]);
}
}
return listResult;
}
}

View File

@@ -0,0 +1,9 @@
namespace CatHasPawsContratcs.Mapper;
[AttributeUsage(AttributeTargets.Property)]
class DefaultValueAttribute : Attribute
{
public object? DefaultValue { get; set; }
public string? FuncName { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace CatHasPawsContratcs.Mapper;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
class PostProcessingAttribute : Attribute
{
public string? MappingCallMethodName { get; set; }
public PostProcessingType ActionType { get; set; } = PostProcessingType.None;
}

View File

@@ -0,0 +1,10 @@
namespace CatHasPawsContratcs.Mapper;
enum PostProcessingType
{
None = -1,
ToUniversalTime = 1,
ToLocalTime = 2
}

View File

@@ -0,0 +1,405 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Этот код создан программой.
// Исполняемая версия:4.0.30319.42000
//
// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
// повторной генерации кода.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CatHasPawsContratcs.Resources {
using System;
/// <summary>
/// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д.
/// </summary>
// Этот класс создан автоматически классом StronglyTypedResourceBuilder
// с помощью такого средства, как ResGen или Visual Studio.
// Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen
// с параметром /str или перестройте свой проект VS.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Messages {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Messages() {
}
/// <summary>
/// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CatHasPawsContratcs.Resources.Messages", typeof(Messages).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Перезаписывает свойство CurrentUICulture текущего потока для всех
/// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Элемент по данным: {0} был удален.
/// </summary>
internal static string AdapterMessageElementDeletedException {
get {
return ResourceManager.GetString("AdapterMessageElementDeletedException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Не найден элемент по данным: {0}.
/// </summary>
internal static string AdapterMessageElementNotFoundException {
get {
return ResourceManager.GetString("AdapterMessageElementNotFoundException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Данные пусты.
/// </summary>
internal static string AdapterMessageEmptyDate {
get {
return ResourceManager.GetString("AdapterMessageEmptyDate", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Неправильные даты: {0}.
/// </summary>
internal static string AdapterMessageIncorrectDatesException {
get {
return ResourceManager.GetString("AdapterMessageIncorrectDatesException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Ошибка при обработке данных: {0}.
/// </summary>
internal static string AdapterMessageInvalidOperationException {
get {
return ResourceManager.GetString("AdapterMessageInvalidOperationException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Ошибка при работе с хранилищем данных: {0}.
/// </summary>
internal static string AdapterMessageStorageException {
get {
return ResourceManager.GetString("AdapterMessageStorageException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Переданы неверные данные: {0}.
/// </summary>
internal static string AdapterMessageValidationException {
get {
return ResourceManager.GetString("AdapterMessageValidationException", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Производитель.
/// </summary>
internal static string DocumentDocCaptionManufacturer {
get {
return ResourceManager.GetString("DocumentDocCaptionManufacturer", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Товар.
/// </summary>
internal static string DocumentDocCaptionProduct {
get {
return ResourceManager.GetString("DocumentDocCaptionProduct", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Продукты по производителям.
/// </summary>
internal static string DocumentDocHeader {
get {
return ResourceManager.GetString("DocumentDocHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Сформировано на дату {0}.
/// </summary>
internal static string DocumentDocSubHeader {
get {
return ResourceManager.GetString("DocumentDocSubHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Кол-во.
/// </summary>
internal static string DocumentExcelCaptionCount {
get {
return ResourceManager.GetString("DocumentExcelCaptionCount", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Дата.
/// </summary>
internal static string DocumentExcelCaptionDate {
get {
return ResourceManager.GetString("DocumentExcelCaptionDate", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Скидка.
/// </summary>
internal static string DocumentExcelCaptionDiscount {
get {
return ResourceManager.GetString("DocumentExcelCaptionDiscount", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Товар.
/// </summary>
internal static string DocumentExcelCaptionProduct {
get {
return ResourceManager.GetString("DocumentExcelCaptionProduct", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Сумма.
/// </summary>
internal static string DocumentExcelCaptionSum {
get {
return ResourceManager.GetString("DocumentExcelCaptionSum", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Всего.
/// </summary>
internal static string DocumentExcelCaptionTotal {
get {
return ResourceManager.GetString("DocumentExcelCaptionTotal", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Продажи за период.
/// </summary>
internal static string DocumentExcelHeader {
get {
return ResourceManager.GetString("DocumentExcelHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на c {0} по {1}.
/// </summary>
internal static string DocumentExcelSubHeader {
get {
return ResourceManager.GetString("DocumentExcelSubHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Начисления.
/// </summary>
internal static string DocumentPdfDiagramCaption {
get {
return ResourceManager.GetString("DocumentPdfDiagramCaption", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Зарплатная ведомость.
/// </summary>
internal static string DocumentPdfHeader {
get {
return ResourceManager.GetString("DocumentPdfHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на за период с {0} по {1}.
/// </summary>
internal static string DocumentPdfSubHeader {
get {
return ResourceManager.GetString("DocumentPdfSubHeader", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Нельзя изменить удаленный элемент (идентификатор: {0}).
/// </summary>
internal static string ElementDeletedExceptionMessage {
get {
return ResourceManager.GetString("ElementDeletedExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Уже существует элемент со значением {0} параметра {1}.
/// </summary>
internal static string ElementExistsExceptionMessage {
get {
return ResourceManager.GetString("ElementExistsExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Элемент не найден по значению = {0}.
/// </summary>
internal static string ElementNotFoundExceptionMessage {
get {
return ResourceManager.GetString("ElementNotFoundExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Дата окончания должна быть позже даты начала. Дата начала: {0}. ​​Дата окончания: {1}.
/// </summary>
internal static string IncorrectDatesExceptionMessage {
get {
return ResourceManager.GetString("IncorrectDatesExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Недостаточно данных для обработки: {0}.
/// </summary>
internal static string NotEnoughDataToProcessExceptionMessage {
get {
return ResourceManager.GetString("NotEnoughDataToProcessExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Не найдены данные.
/// </summary>
internal static string NotFoundDataMessage {
get {
return ResourceManager.GetString("NotFoundDataMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Ошибка при работе в хранилище: {0}.
/// </summary>
internal static string StorageExceptionMessage {
get {
return ResourceManager.GetString("StorageExceptionMessage", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Дата трудоустройства не может быть раньше даты рождения ({0}, {1}).
/// </summary>
internal static string ValidationExceptionMessageEmploymentDateAndBirthDate {
get {
return ResourceManager.GetString("ValidationExceptionMessageEmploymentDateAndBirthDate", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} пусто.
/// </summary>
internal static string ValidationExceptionMessageEmptyField {
get {
return ResourceManager.GetString("ValidationExceptionMessageEmptyField", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле Телефонный номер не является телефонным номером.
/// </summary>
internal static string ValidationExceptionMessageIncorrectPhoneNumber {
get {
return ResourceManager.GetString("ValidationExceptionMessageIncorrectPhoneNumber", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} меньше или равно 0.
/// </summary>
internal static string ValidationExceptionMessageLessOrEqualZero {
get {
return ResourceManager.GetString("ValidationExceptionMessageLessOrEqualZero", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Несовершеннолетние не могут быть приняты на работу (Дата рождения: {0}).
/// </summary>
internal static string ValidationExceptionMessageMinorsBirthDate {
get {
return ResourceManager.GetString("ValidationExceptionMessageMinorsBirthDate", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Несовершеннолетние не могут быть приняты на работу (Дата трудоустройства {0}, Дата рождения: {1}).
/// </summary>
internal static string ValidationExceptionMessageMinorsEmploymentDate {
get {
return ResourceManager.GetString("ValidationExceptionMessageMinorsEmploymentDate", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на В продаже должен быть хотя бы один товар.
/// </summary>
internal static string ValidationExceptionMessageNoProductsInSale {
get {
return ResourceManager.GetString("ValidationExceptionMessageNoProductsInSale", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} не является типом уникального идентификатора.
/// </summary>
internal static string ValidationExceptionMessageNotAId {
get {
return ResourceManager.GetString("ValidationExceptionMessageNotAId", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} не проиницализировано.
/// </summary>
internal static string ValidationExceptionMessageNotInitialized {
get {
return ResourceManager.GetString("ValidationExceptionMessageNotInitialized", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DocumentDocCaptionManufacturer" xml:space="preserve">
<value>Manufacturer</value>
</data>
<data name="DocumentDocCaptionProduct" xml:space="preserve">
<value>Product</value>
</data>
<data name="DocumentDocHeader" xml:space="preserve">
<value>Products by Manufacturer</value>
</data>
<data name="DocumentDocSubHeader" xml:space="preserve">
<value>Generated on date {0}</value>
</data>
<data name="DocumentExcelCaptionCount" xml:space="preserve">
<value>Count</value>
</data>
<data name="DocumentExcelCaptionDate" xml:space="preserve">
<value>Date</value>
</data>
<data name="DocumentExcelCaptionDiscount" xml:space="preserve">
<value>Discount</value>
</data>
<data name="DocumentExcelCaptionProduct" xml:space="preserve">
<value>Product</value>
</data>
<data name="DocumentExcelCaptionSum" xml:space="preserve">
<value>Sum</value>
</data>
<data name="DocumentExcelCaptionTotal" xml:space="preserve">
<value>Total</value>
</data>
<data name="DocumentExcelHeader" xml:space="preserve">
<value>Sales for the period</value>
</data>
<data name="DocumentExcelSubHeader" xml:space="preserve">
<value>from {0} to {1}</value>
</data>
<data name="DocumentPdfHeader" xml:space="preserve">
<value>Payroll</value>
</data>
<data name="DocumentPdfSubHeader" xml:space="preserve">
<value>for the period from {0} to {1}</value>
</data>
<data name="DocumentPdfDiagramCaption" xml:space="preserve">
<value>Accruals</value>
</data>
<data name="NotFoundDataMessage" xml:space="preserve">
<value>No data found</value>
</data>
<data name="ValidationExceptionMessageNotAId" xml:space="preserve">
<value>The value in the {0} field is not a unique identifier type.</value>
</data>
<data name="ValidationExceptionMessageEmptyField" xml:space="preserve">
<value>The value in field {0} is empty</value>
</data>
<data name="ValidationExceptionMessageIncorrectPhoneNumber" xml:space="preserve">
<value>The value in the Phone Number field is not a phone number.</value>
</data>
<data name="ValidationExceptionMessageNotInitialized" xml:space="preserve">
<value>The value in field {0} is not initialized</value>
</data>
<data name="ValidationExceptionMessageLessOrEqualZero" xml:space="preserve">
<value>The value in field {0} is less than or equal to 0</value>
</data>
<data name="ValidationExceptionMessageNoProductsInSale" xml:space="preserve">
<value>There must be at least one item on sale</value>
</data>
<data name="ValidationExceptionMessageMinorsBirthDate" xml:space="preserve">
<value>Minors cannot be hired (BirthDate = {0})</value>
</data>
<data name="ValidationExceptionMessageMinorsEmploymentDate" xml:space="preserve">
<value>Minors cannot be hired (EmploymentDate: {0}, BirthDate {1})</value>
</data>
<data name="ValidationExceptionMessageEmploymentDateAndBirthDate" xml:space="preserve">
<value>Date of employment cannot be earlier than date of birth ({0}, {1})</value>
</data>
<data name="AdapterMessageStorageException" xml:space="preserve">
<value>Error while working with data storage: {0}</value>
</data>
<data name="AdapterMessageEmptyDate" xml:space="preserve">
<value>Data is empty</value>
</data>
<data name="AdapterMessageElementNotFoundException" xml:space="preserve">
<value>Not found element by data: {0}</value>
</data>
<data name="AdapterMessageValidationException" xml:space="preserve">
<value>Incorrect data transmitted: {0}</value>
</data>
<data name="AdapterMessageElementDeletedException" xml:space="preserve">
<value>Element by data: {0} was deleted</value>
</data>
<data name="AdapterMessageInvalidOperationException" xml:space="preserve">
<value>Error processing data: {0}</value>
</data>
<data name="AdapterMessageIncorrectDatesException" xml:space="preserve">
<value>Incorrect dates: {0}</value>
</data>
<data name="ElementDeletedExceptionMessage" xml:space="preserve">
<value>Cannot modify a deleted item (id: {0})</value>
</data>
<data name="ElementExistsExceptionMessage" xml:space="preserve">
<value>There is already an element with value {0} of parameter {1}</value>
</data>
<data name="ElementNotFoundExceptionMessage" xml:space="preserve">
<value>Element not found at value = {0}</value>
</data>
<data name="IncorrectDatesExceptionMessage" xml:space="preserve">
<value>The end date must be later than the start date.. StartDate: {0}. EndDate: {1}</value>
</data>
<data name="NotEnoughDataToProcessExceptionMessage" xml:space="preserve">
<value>Not enough data to process: {0}</value>
</data>
<data name="StorageExceptionMessage" xml:space="preserve">
<value>Error while working in storage: {0}</value>
</data>
</root>

View File

@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DocumentDocCaptionManufacturer" xml:space="preserve">
<value>Производитель</value>
</data>
<data name="DocumentDocCaptionProduct" xml:space="preserve">
<value>Товар</value>
</data>
<data name="DocumentDocHeader" xml:space="preserve">
<value>Продукты по производителям</value>
</data>
<data name="DocumentDocSubHeader" xml:space="preserve">
<value>Сформировано на дату {0}</value>
</data>
<data name="DocumentExcelCaptionCount" xml:space="preserve">
<value>Кол-во</value>
</data>
<data name="DocumentExcelCaptionDate" xml:space="preserve">
<value>Дата</value>
</data>
<data name="DocumentExcelCaptionDiscount" xml:space="preserve">
<value>Скидка</value>
</data>
<data name="DocumentExcelCaptionProduct" xml:space="preserve">
<value>Товар</value>
</data>
<data name="DocumentExcelCaptionSum" xml:space="preserve">
<value>Сумма</value>
</data>
<data name="DocumentExcelCaptionTotal" xml:space="preserve">
<value>Всего</value>
</data>
<data name="DocumentExcelHeader" xml:space="preserve">
<value>Продажи за период</value>
</data>
<data name="DocumentExcelSubHeader" xml:space="preserve">
<value>c {0} по {1}</value>
</data>
<data name="DocumentPdfHeader" xml:space="preserve">
<value>Зарплатная ведомость</value>
</data>
<data name="DocumentPdfSubHeader" xml:space="preserve">
<value>за период с {0} по {1}</value>
</data>
<data name="DocumentPdfDiagramCaption" xml:space="preserve">
<value>Начисления</value>
</data>
<data name="NotFoundDataMessage" xml:space="preserve">
<value>Не найдены данные</value>
</data>
<data name="ValidationExceptionMessageNotAId" xml:space="preserve">
<value>Значение в поле {0} не является типом уникального идентификатора</value>
</data>
<data name="ValidationExceptionMessageEmptyField" xml:space="preserve">
<value>Значение в поле {0} пусто</value>
</data>
<data name="ValidationExceptionMessageIncorrectPhoneNumber" xml:space="preserve">
<value>Значение в поле Телефонный номер не является телефонным номером</value>
</data>
<data name="ValidationExceptionMessageNotInitialized" xml:space="preserve">
<value>Значение в поле {0} не проиницализировано</value>
</data>
<data name="ValidationExceptionMessageLessOrEqualZero" xml:space="preserve">
<value>Значение в поле {0} меньше или равно 0</value>
</data>
<data name="ValidationExceptionMessageNoProductsInSale" xml:space="preserve">
<value>В продаже должен быть хотя бы один товар</value>
</data>
<data name="ValidationExceptionMessageMinorsBirthDate" xml:space="preserve">
<value>Несовершеннолетние не могут быть приняты на работу (Дата рождения: {0})</value>
</data>
<data name="ValidationExceptionMessageMinorsEmploymentDate" xml:space="preserve">
<value>Несовершеннолетние не могут быть приняты на работу (Дата трудоустройства {0}, Дата рождения: {1})</value>
</data>
<data name="ValidationExceptionMessageEmploymentDateAndBirthDate" xml:space="preserve">
<value>Дата трудоустройства не может быть раньше даты рождения ({0}, {1})</value>
</data>
<data name="AdapterMessageStorageException" xml:space="preserve">
<value>Ошибка при работе с хранилищем данных: {0}</value>
</data>
<data name="AdapterMessageEmptyDate" xml:space="preserve">
<value>Данные пусты</value>
</data>
<data name="AdapterMessageElementNotFoundException" xml:space="preserve">
<value>Не найден элемент по данным: {0}</value>
</data>
<data name="AdapterMessageValidationException" xml:space="preserve">
<value>Переданы неверные данные: {0}</value>
</data>
<data name="AdapterMessageElementDeletedException" xml:space="preserve">
<value>Элемент по данным: {0} был удален</value>
</data>
<data name="AdapterMessageInvalidOperationException" xml:space="preserve">
<value>Ошибка при обработке данных: {0}</value>
</data>
<data name="AdapterMessageIncorrectDatesException" xml:space="preserve">
<value>Неправильные даты: {0}</value>
</data>
<data name="ElementDeletedExceptionMessage" xml:space="preserve">
<value>Нельзя изменить удаленный элемент (идентификатор: {0})</value>
</data>
<data name="ElementExistsExceptionMessage" xml:space="preserve">
<value>Уже существует элемент со значением {0} параметра {1}</value>
</data>
<data name="ElementNotFoundExceptionMessage" xml:space="preserve">
<value>Элемент не найден по значению = {0}</value>
</data>
<data name="IncorrectDatesExceptionMessage" xml:space="preserve">
<value>Дата окончания должна быть позже даты начала. Дата начала: {0}. ​​Дата окончания: {1}</value>
</data>
<data name="NotEnoughDataToProcessExceptionMessage" xml:space="preserve">
<value>Недостаточно данных для обработки: {0}</value>
</data>
<data name="StorageExceptionMessage" xml:space="preserve">
<value>Ошибка при работе в хранилище: {0}</value>
</data>
</root>

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface IBuyerStorageContract
{
List<BuyerDataModel> GetList();
BuyerDataModel? GetElementById(string id);
BuyerDataModel? GetElementByPhoneNumber(string phoneNumber);
BuyerDataModel? GetElementByFIO(string fio);
void AddElement(BuyerDataModel buyerDataModel);
void UpdElement(BuyerDataModel buyerDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface IManufacturerStorageContract
{
List<ManufacturerDataModel> GetList();
ManufacturerDataModel? GetElementById(string id);
ManufacturerDataModel? GetElementByName(string name);
ManufacturerDataModel? GetElementByOldName(string name);
void AddElement(ManufacturerDataModel manufacturerDataModel);
void UpdElement(ManufacturerDataModel manufacturerDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,22 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface IPostStorageContract
{
List<PostDataModel> GetList();
List<PostDataModel> GetPostWithHistory(string postId);
PostDataModel? GetElementById(string id);
PostDataModel? GetElementByName(string name);
void AddElement(PostDataModel postDataModel);
void UpdElement(PostDataModel postDataModel);
void DelElement(string id);
void ResElement(string id);
}

View File

@@ -0,0 +1,22 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface IProductStorageContract
{
List<ProductDataModel> GetList(bool onlyActive = true, string? manufacturerId = null);
Task<List<ProductDataModel>> GetListAsync(CancellationToken ct);
List<ProductHistoryDataModel> GetHistoryByProductId(string productId);
ProductDataModel? GetElementById(string id);
ProductDataModel? GetElementByName(string name);
void AddElement(ProductDataModel productDataModel);
void UpdElement(ProductDataModel productDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,12 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface ISalaryStorageContract
{
List<SalaryDataModel> GetList(DateTime startDate, DateTime endDate, string? workerId = null);
Task<List<SalaryDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
void AddElement(SalaryDataModel salaryDataModel);
}

View File

@@ -0,0 +1,16 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface ISaleStorageContract
{
List<SaleDataModel> GetList(DateTime? startDate = null, DateTime? endDate = null, string? workerId = null, string? buyerId = null, string? productId = null);
Task<List<SaleDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
SaleDataModel? GetElementById(string id);
void AddElement(SaleDataModel saleDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,20 @@
using CatHasPawsContratcs.DataModels;
namespace CatHasPawsContratcs.StoragesContracts;
internal interface IWorkerStorageContract
{
List<WorkerDataModel> GetList(bool onlyActive = true, string? postId = null, DateTime? fromBirthDate = null, DateTime? toBirthDate = null, DateTime? fromEmploymentDate = null, DateTime? toEmploymentDate = null);
WorkerDataModel? GetElementById(string id);
WorkerDataModel? GetElementByFIO(string fio);
void AddElement(WorkerDataModel workerDataModel);
void UpdElement(WorkerDataModel workerDataModel);
void DelElement(string id);
int GetWorkerTrend(DateTime fromPeriod, DateTime toPeriod);
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.ViewModels;
public class BuyerViewModel
{
public required string Id { get; set; }
public required string FIO { get; set; }
public required string PhoneNumber { get; set; }
public double DiscountSize { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace CatHasPawsContratcs.ViewModels;
public class ManufacturerProductViewModel
{
public required string ManufacturerName { get; set; }
public required List<string> Products { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.ViewModels;
public class ManufacturerViewModel
{
public required string Id { get; set; }
public required string ManufacturerName { get; set; }
public string? PrevManufacturerName { get; set; }
public string? PrevPrevManufacturerName { get; set; }
}

View File

@@ -0,0 +1,21 @@
using CatHasPawsContratcs.Infrastructure.PostConfigurations;
using CatHasPawsContratcs.Mapper;
using System.Text.Json;
namespace CatHasPawsContratcs.ViewModels;
public class PostViewModel
{
public required string Id { get; set; }
public required string PostName { get; set; }
public required string PostType { get; set; }
[AlternativeName("ConfigurationModel")]
[PostProcessing(MappingCallMethodName = "ParseConfiguration")]
public required string Configuration { get; set; }
private string ParseConfiguration(PostConfiguration model) =>
JsonSerializer.Serialize(model, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}

View File

@@ -0,0 +1,10 @@
namespace CatHasPawsContratcs.ViewModels;
public class ProductHistoryViewModel
{
public required string ProductName { get; set; }
public double OldPrice { get; set; }
public DateTime ChangeDate { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace CatHasPawsContratcs.ViewModels;
public class ProductViewModel
{
public required string Id { get; set; }
public required string ProductName { get; set; }
public required string ManufacturerId { get; set; }
public required string ManufacturerName { get; set; }
public required string ProductType { get; set; }
public double Price { get; set; }
public bool IsDeleted { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.ViewModels;
public class SaleProductViewModel
{
public required string ProductId { get; set; }
public required string ProductName { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,26 @@
namespace CatHasPawsContratcs.ViewModels;
public class SaleViewModel
{
public required string Id { get; set; }
public required string WorkerId { get; set; }
public required string WorkerFIO { get; set; }
public string? BuyerId { get; set; }
public string? BuyerFIO { get; set; }
public DateTime SaleDate { get; set; }
public double Sum { get; set; }
public required string DiscountType { get; set; }
public double Discount { get; set; }
public bool IsCancel { get; set; }
public required List<SaleProductViewModel> Products { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace CatHasPawsContratcs.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

@@ -0,0 +1,18 @@
namespace CatHasPawsContratcs.ViewModels;
public class WorkerViewModel
{
public required string Id { get; set; }
public required string FIO { get; set; }
public required string PostId { get; set; }
public required string PostName { get; set; }
public bool IsDeleted { get; set; }
public DateTime BirthDate { get; set; }
public DateTime EmploymentDate { get; set; }
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2">
<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.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CatHasPawsContratcs\CatHasPawsContratcs.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="CatHasPawsWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,97 @@
using CatHasPawsContratcs.Infrastructure;
using CatHasPawsContratcs.Infrastructure.PostConfigurations;
using CatHasPawsDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace CatHasPawsDatabase;
internal class CatHasPawsDbContext : DbContext
{
private readonly IConfigurationDatabase? _configurationDatabase;
public CatHasPawsDbContext(IConfigurationDatabase configurationDatabase)
{
_configurationDatabase = configurationDatabase;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(_configurationDatabase?.ConnectionString, o => o.SetPostgresVersion(12, 2));
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Buyer>().HasIndex(x => x.PhoneNumber).IsUnique();
modelBuilder.Entity<Sale>()
.HasOne(e => e.Buyer)
.WithMany(e => e.Sales)
.OnDelete(DeleteBehavior.SetNull);
modelBuilder.Entity<Manufacturer>().HasIndex(x => x.ManufacturerName).IsUnique();
modelBuilder.Entity<Post>()
.HasIndex(e => new { e.PostName, e.IsActual })
.IsUnique()
.HasFilter($"\"{nameof(Post.IsActual)}\" = TRUE");
modelBuilder.Entity<Post>()
.HasIndex(e => new { e.PostId, e.IsActual })
.IsUnique()
.HasFilter($"\"{nameof(Post.IsActual)}\" = TRUE");
modelBuilder.Entity<Product>()
.HasIndex(x => new { x.ProductName, x.IsDeleted })
.IsUnique()
.HasFilter($"\"{nameof(Product.IsDeleted)}\" = FALSE");
modelBuilder
.Entity<Product>()
.HasOne(e => e.Manufacturer)
.WithMany(e => e.Products)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<SaleProduct>().HasKey(x => new { x.SaleId, x.ProductId });
modelBuilder.Entity<Post>()
.Property(x => x.Configuration)
.HasColumnType("jsonb")
.HasConversion(
x => SerializePostConfiguration(x),
x => DeserialzePostConfiguration(x)
);
}
public DbSet<Buyer> Buyers { get; set; }
public DbSet<Manufacturer> Manufacturers { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ProductHistory> ProductHistories { get; set; }
public DbSet<Salary> Salaries { get; set; }
public DbSet<Sale> Sales { get; set; }
public DbSet<SaleProduct> SaleProducts { get; set; }
public DbSet<Worker> Workers { get; set; }
private static string SerializePostConfiguration(PostConfiguration postConfiguration) => JsonConvert.SerializeObject(postConfiguration);
private static PostConfiguration DeserialzePostConfiguration(string jsonString) => JToken.Parse(jsonString).Value<string>("Type") switch
{
nameof(CashierPostConfiguration) => JsonConvert.DeserializeObject<CashierPostConfiguration>(jsonString)!,
nameof(SupervisorPostConfiguration) => JsonConvert.DeserializeObject<SupervisorPostConfiguration>(jsonString)!,
_ => JsonConvert.DeserializeObject<PostConfiguration>(jsonString)!,
};
}

View File

@@ -0,0 +1,8 @@
using CatHasPawsContratcs.Infrastructure;
namespace CatHasPawsDatabase;
class DefaultConfigurationDatabase : IConfigurationDatabase
{
public string ConnectionString => "";
}

View File

@@ -0,0 +1,145 @@
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Mapper;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using CatHasPawsDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using Npgsql;
namespace CatHasPawsDatabase.Implementations;
internal class BuyerStorageContract(CatHasPawsDbContext catHasPawsDbContext, IStringLocalizer<Messages> localizer) : IBuyerStorageContract
{
private readonly CatHasPawsDbContext _dbContext = catHasPawsDbContext;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<BuyerDataModel> GetList()
{
try
{
return [.. _dbContext.Buyers.Select(x => CustomMapper.MapObject<BuyerDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public BuyerDataModel? GetElementById(string id)
{
try
{
return CustomMapper.MapObjectWithNull<BuyerDataModel>(GetBuyerById(id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public BuyerDataModel? GetElementByFIO(string fio)
{
try
{
return CustomMapper.MapObjectWithNull<BuyerDataModel>(_dbContext.Buyers.FirstOrDefault(x => x.FIO == fio));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public BuyerDataModel? GetElementByPhoneNumber(string phoneNumber)
{
try
{
return CustomMapper.MapObjectWithNull<BuyerDataModel>(_dbContext.Buyers.FirstOrDefault(x => x.PhoneNumber == phoneNumber));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void AddElement(BuyerDataModel buyerDataModel)
{
try
{
_dbContext.Buyers.Add(CustomMapper.MapObject<Buyer>(buyerDataModel));
_dbContext.SaveChanges();
}
catch (InvalidOperationException ex) when (ex.TargetSite?.Name == "ThrowIdentityConflict")
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("Id", buyerDataModel.Id, _localizer);
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Buyers_PhoneNumber" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("PhoneNumber", buyerDataModel.PhoneNumber, _localizer);
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "PK_Buyers" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("Id", buyerDataModel.Id, _localizer);
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void UpdElement(BuyerDataModel buyerDataModel)
{
try
{
var element = GetBuyerById(buyerDataModel.Id) ?? throw new ElementNotFoundException(buyerDataModel.Id, _localizer);
_dbContext.Buyers.Update(CustomMapper.MapObject(buyerDataModel, element));
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Buyers_PhoneNumber" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("PhoneNumber", buyerDataModel.PhoneNumber, _localizer);
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void DelElement(string id)
{
try
{
var element = GetBuyerById(id) ?? throw new ElementNotFoundException(id, _localizer);
_dbContext.Buyers.Remove(element);
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
private Buyer? GetBuyerById(string id) => _dbContext.Buyers.FirstOrDefault(x => x.Id == id);
}

View File

@@ -0,0 +1,165 @@
using AutoMapper;
using CatHasPawsContratcs.DataModels;
using CatHasPawsContratcs.Exceptions;
using CatHasPawsContratcs.Resources;
using CatHasPawsContratcs.StoragesContracts;
using CatHasPawsDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Localization;
using Npgsql;
namespace CatHasPawsDatabase.Implementations;
internal class ManufacturerStorageContract : IManufacturerStorageContract
{
private readonly CatHasPawsDbContext _dbContext;
private readonly Mapper _mapper;
private readonly IStringLocalizer<Messages> _localizer;
public ManufacturerStorageContract(CatHasPawsDbContext dbContext, IStringLocalizer<Messages> localizer)
{
_dbContext = dbContext;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Manufacturer, ManufacturerDataModel>();
cfg.CreateMap<ManufacturerDataModel, Manufacturer>();
});
_mapper = new Mapper(config);
_localizer = localizer;
}
public List<ManufacturerDataModel> GetList()
{
try
{
return [.. _dbContext.Manufacturers.Select(x => _mapper.Map<ManufacturerDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public ManufacturerDataModel? GetElementById(string id)
{
try
{
return _mapper.Map<ManufacturerDataModel>(GetManufacturerById(id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public ManufacturerDataModel? GetElementByName(string name)
{
try
{
return _mapper.Map<ManufacturerDataModel>(_dbContext.Manufacturers.FirstOrDefault(x => x.ManufacturerName == name));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public ManufacturerDataModel? GetElementByOldName(string name)
{
try
{
return _mapper.Map<ManufacturerDataModel>(_dbContext.Manufacturers.FirstOrDefault(x => x.PrevManufacturerName == name ||
x.PrevPrevManufacturerName == name));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void AddElement(ManufacturerDataModel manufacturerDataModel)
{
try
{
_dbContext.Manufacturers.Add(_mapper.Map<Manufacturer>(manufacturerDataModel));
_dbContext.SaveChanges();
}
catch (InvalidOperationException ex) when (ex.TargetSite?.Name == "ThrowIdentityConflict")
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("Id", manufacturerDataModel.Id, _localizer);
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Manufacturers_ManufacturerName" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("ManufacturerName", manufacturerDataModel.ManufacturerName, _localizer);
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "PK_Manufacturers" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("Id", manufacturerDataModel.Id, _localizer);
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void UpdElement(ManufacturerDataModel manufacturerDataModel)
{
try
{
var element = GetManufacturerById(manufacturerDataModel.Id) ?? throw new ElementNotFoundException(manufacturerDataModel.Id, _localizer);
if (element.ManufacturerName != manufacturerDataModel.ManufacturerName)
{
element.PrevPrevManufacturerName = element.PrevManufacturerName;
element.PrevManufacturerName = element.ManufacturerName;
element.ManufacturerName = manufacturerDataModel.ManufacturerName;
}
_dbContext.Manufacturers.Update(element);
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Manufacturers_ManufacturerName" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException("ManufacturerName", manufacturerDataModel.ManufacturerName, _localizer);
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
public void DelElement(string id)
{
try
{
var element = GetManufacturerById(id) ?? throw new ElementNotFoundException(id, _localizer);
_dbContext.Manufacturers.Remove(element);
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex, _localizer);
}
}
private Manufacturer? GetManufacturerById(string id) => _dbContext.Manufacturers.FirstOrDefault(x => x.Id == id);
}

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