34 Commits

Author SHA1 Message Date
qkrlnt
082f0b8516 done 2025-05-29 03:11:34 +04:00
qkrlnt
d1211c2a4f mapper 2025-05-28 23:02:08 +04:00
qkrlnt
a066616bf1 lab_7_done 2025-05-28 22:30:16 +04:00
qkrlnt
d7000b820d check locale 2025-05-28 21:43:05 +04:00
qkrlnt
7abcaae667 strings 2025-05-27 14:02:08 +04:00
qkrlnt
c0bba87e7b Dates 2025-05-26 18:56:55 +04:00
qkrlnt
aa98b01866 Merge branch 'Task_6_Reports' into Task_7_Locale 2025-05-26 18:32:03 +04:00
qkrlnt
ed64f878f9 SomeChanges 2025-05-26 18:31:09 +04:00
qkrlnt
9485be3564 some changes after 6th 2025-05-26 18:29:00 +04:00
qkrlnt
8bd6c07099 готово...... 2025-05-26 16:49:59 +04:00
qkrlnt
f65d7761cc ExcelDone 2025-05-26 10:17:24 +04:00
qkrlnt
81532bbf89 getDataSaleByPeriod 2025-05-25 19:02:04 +04:00
qkrlnt
2a80cac3cb 1 этап готов 2025-05-25 12:36:40 +04:00
qkrlnt
6427a6f90f чуть чуть не доделалэтап 2025-05-17 09:33:58 +04:00
qkrlnt
fbac837910 Prepare 2025-05-16 21:32:53 +04:00
qkrlnt
983c280349 5лабораторная! 2025-05-16 18:18:29 +04:00
qkrlnt
57cf475947 Изменения в услугах 2025-05-15 01:33:26 +04:00
qkrlnt
565ee0d356 Миграция 2025-05-13 14:12:24 +04:00
137102521e merge 2025-04-23 02:17:37 +04:00
4d35d3bbd7 слил изменения из 3 лабораторной (были добавлены после завершения 4) 2025-04-23 02:17:19 +04:00
aea4a38d43 конец. (очень надеюсь) 2025-04-23 01:57:33 +04:00
59237098e6 добавил еще 3 теста 2025-04-23 01:20:34 +04:00
32ca540467 Почти доделал 2025-04-23 01:02:42 +04:00
4fcf355c8c Worker, Material 2025-04-21 18:37:01 +04:00
ee542b8ff8 2тесты 2025-04-19 11:01:51 +04:00
657cc2cc66 переделал маппинг под требования в Car и Worker 2025-04-17 20:59:41 +04:00
568db1298b тестики 2025-04-17 20:40:42 +04:00
7a3e713cf6 Контракты 2025-04-14 00:32:42 +04:00
38f7939e6f Модели 2025-04-05 00:58:40 +04:00
82eb5c45d9 Немного тестов 2025-03-11 22:31:07 +04:00
3531c73bb5 Заготовки реализаций 2025-02-22 09:58:18 +04:00
03ebddb64d Контракты 2025-02-22 00:34:47 +04:00
42752cb298 Создание тестов 2025-02-05 23:46:13 +04:00
c9e8eebca6 Создание моделей 2025-02-05 23:44:00 +04:00
196 changed files with 16958 additions and 1 deletions

View File

@@ -0,0 +1,96 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
namespace PimpMyRideBusinessLogic.Implementations;
internal class CarBusinessLogicContract(ICarStorageContract carStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : ICarBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly ICarStorageContract _carStorageContract = carStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<CarDataModel> GetAllCars()
{
_logger.LogInformation("GetAllCars");
return _carStorageContract.GetList();
}
public List<CarDataModel> GetAllCarsByClient(string clientId)
{
_logger.LogInformation("GetAllCars params: {clientId}", clientId);
if (clientId.IsEmpty())
{
throw new ArgumentNullException(nameof(clientId));
}
if (!clientId.IsGuid())
{
throw new ValidationException("The value in the field clientId is not a unique identifier.");
}
return _carStorageContract.GetList(clientId);
}
public List<CarDataModel> GetAllCarsByMake(string make, string model)
{
_logger.LogInformation("GetAllCars params: {make} {model}", make, model);
if (make.IsEmpty())
{
throw new ArgumentNullException(nameof(make));
}
if (model.IsEmpty())
{
throw new ArgumentNullException(nameof(model));
}
return _carStorageContract.GetList(make: make, model: model);
}
public CarDataModel GetCarByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _carStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _carStorageContract.GetElementByStateNumber(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertCar(CarDataModel carDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(carDataModel));
ArgumentNullException.ThrowIfNull(carDataModel);
carDataModel.Validate(_localizer);
_carStorageContract.AddElement(carDataModel);
}
public void UpdateCar(CarDataModel carDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(carDataModel));
ArgumentNullException.ThrowIfNull(carDataModel);
carDataModel.Validate(_localizer);
_carStorageContract.UpdElement(carDataModel);
}
public void DeleteCar(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
_carStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,73 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace PimpMyRideBusinessLogic.Implementations;
internal class ClientBusinessLogicContract(IClientStorageContract clientStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IClientBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IClientStorageContract _clientStorageContract = clientStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<ClientDataModel> GetAllClients()
{
_logger.LogInformation("GetAllClients");
return _clientStorageContract.GetList();
}
public ClientDataModel GetClientByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _clientStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
if (Regex.IsMatch(data, @"^((\+7|7|8)+([0-9]){10})$"))
{
return _clientStorageContract.GetElementByPhoneNumber(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _clientStorageContract.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertClient(ClientDataModel clientDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(clientDataModel));
ArgumentNullException.ThrowIfNull(clientDataModel);
clientDataModel.Validate(_localizer);
_clientStorageContract.AddElement(clientDataModel);
}
public void UpdateClient(ClientDataModel clientDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(clientDataModel));
ArgumentNullException.ThrowIfNull(clientDataModel);
clientDataModel.Validate(_localizer);
_clientStorageContract.UpdElement(clientDataModel);
}
public void DeleteClient(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
_clientStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,93 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
namespace PimpMyRideBusinessLogic.Implementations;
internal class MaterialBusinessLogicContract(IMaterialStorageContracts materialStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IMaterialBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IMaterialStorageContracts _materialStorageContract = materialStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<MaterialDataModel> GetAllMaterials()
{
_logger.LogInformation("GetAllMaterials");
return _materialStorageContract.GetList();
}
public List<MaterialDataModel> GetAllMaterialsByType(MaterialType materialType)
{
_logger.LogInformation("GetAllMaterials params: {materialType}", materialType);
if (materialType == MaterialType.None)
{
throw new ArgumentNullException(nameof(materialType));
}
return _materialStorageContract.GetList(materialType);
}
public MaterialDataModel GetMaterialByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _materialStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _materialStorageContract.GetElementByDescription(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public List<MaterialHistoryDataModel> GetMaterialHistoryById(string materialId)
{
_logger.LogInformation("GetMaterialHistoryById for {materialId}", materialId);
if (materialId.IsEmpty())
{
throw new ArgumentNullException(nameof(materialId));
}
if (!materialId.IsGuid())
{
throw new ValidationException("The value in the field materialId is not a unique identifier.");
}
return _materialStorageContract.GetHistoryByMaterialId(materialId);
}
public void InsertMaterial(MaterialDataModel materialDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(materialDataModel));
ArgumentNullException.ThrowIfNull(materialDataModel);
materialDataModel.Validate(_localizer);
_materialStorageContract.AddElement(materialDataModel);
}
public void UpdateMaterial(MaterialDataModel materialDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(materialDataModel));
ArgumentNullException.ThrowIfNull(materialDataModel);
materialDataModel.Validate(_localizer);
_materialStorageContract.UpdElement(materialDataModel);
}
public void DeleteMaterial(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
_materialStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,149 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
namespace PimpMyRideBusinessLogic.Implementations;
internal class OrderBusinessLogicContract(IOrderStorageContract orderStorageContract, IServiceStorageContracts serviceStorageContract,
IServiceBusinessLogicContract serviceLogic, IConfigurationOrder configuration, IStringLocalizer<Messages> localizer, ILogger logger) : IOrderBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IOrderStorageContract _orderStorageContract = orderStorageContract;
private readonly IServiceStorageContracts _serviceStorage = serviceStorageContract;
private readonly IServiceBusinessLogicContract _serviceLogic = serviceLogic;
private readonly IConfigurationOrder _config = configuration;
private readonly object _lockObject = new();
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<OrderDataModel> GetAllOrdersByCarByPeriod(string carId, DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllOrders params: {carId}, {fromDate}, {toDate}", carId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (carId.IsEmpty())
{
throw new ArgumentNullException(nameof(carId));
}
if (!carId.IsGuid())
{
throw new ValidationException("The value in the field carId is not a unique identifier.");
}
return _orderStorageContract.GetList(fromDate, toDate, carId: carId);
}
public List<OrderDataModel> GetAllOrdersByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {fromDate}, {toDate}", fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _orderStorageContract.GetList(fromDate, toDate);
}
public OrderDataModel GetOrderByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (!data.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
return _orderStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertOrder(OrderDataModel orderDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(orderDataModel));
ArgumentNullException.ThrowIfNull(orderDataModel);
orderDataModel.Validate(_localizer);
_orderStorageContract.AddElement(orderDataModel);
}
public void CancelOrder(string id)
{
_logger.LogInformation("Cancel by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
_orderStorageContract.DelElement(id);
}
public double CalculateOrderCost(string orderId)
{
_logger.LogInformation("CalculateOrderCost for order {orderId}", orderId);
var order = _orderStorageContract.GetElementById(orderId)
?? throw new ElementNotFoundException(orderId, _localizer);
var services = order.Services;
if (services.Count == 0) throw new ValidationException("Order must include services");
double totalCost = 0;
var tasks = new List<Task>();
var semaphore = new Semaphore(1, _config.MaxThreads);
foreach (var orderSvc in services)
{
semaphore.WaitOne();
tasks.Add(Task.Factory.StartNew((object? obj) =>
{
var svcMapping = (OrderServiceDataModel)obj!;
try
{
var service = _serviceStorage.GetElementById(svcMapping.ServiceId)
?? throw new ElementNotFoundException(svcMapping.ServiceId, _localizer);
double cost = _serviceLogic.CalculateTotalCost(service);
lock (_lockObject)
{
totalCost += cost;
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error calculating cost for service {serviceId}", svcMapping.ServiceId);
}
}, orderSvc));
semaphore.Release();
}
try
{
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
_logger.LogError(ex, "Aggregate error in parallel cost calculations");
}
_logger.LogDebug("Order {orderId} total cost = {totalCost}", orderId, totalCost);
return totalCost;
}
}

View File

@@ -0,0 +1,128 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideBusinessLogic.OfficePackage;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
namespace PimpMyRideBusinessLogic.Implementations;
internal class ReportContract : IReportContract
{
private readonly ICarStorageContract _carStorageContract;
private readonly IOrderStorageContract _orderStorageContract;
private readonly IServiceStorageContracts _serviceStorageContract;
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(ICarStorageContract carStorageContract, IOrderStorageContract orderStorageContract, IServiceStorageContracts serviceStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, IStringLocalizer<Messages> localizer, ILogger logger)
{
_carStorageContract = carStorageContract;
_orderStorageContract = orderStorageContract;
_serviceStorageContract = serviceStorageContract;
_baseWordBuilder = baseWordBuilder;
_baseExcelBuilder = baseExcelBuilder;
_basePdfBuilder = basePdfBuilder;
_localizer = localizer;
_logger = logger;
_documentHeader = [_localizer["DocumentDocCaptionClient"], _localizer["DocumentDocCaptionCar"]];
_tableHeader = [_localizer["DocumentExcelCaptionDate"], _localizer["DocumentExcelCaptionSum"], "",
_localizer["DocumentExcelCaptionMaterial"], _localizer["DocumentExcelCaptionCount"]];
}
public async Task<Stream> CreateDocumentCarsByClientAsync(CancellationToken ct)
{
_logger.LogInformation("Create data CarsByClient");
var data = await GetDataByCarsAsync(ct);
var tableRows = data.SelectMany(x =>
(new List<string[]> { new string[] { x.ClientFIO, "" } })
.Union(x.Cars.Select(y => new string[] { "", y }))
).ToList();
tableRows.Insert(0, _documentHeader);
return _baseWordBuilder
.AddHeader(_localizer["DocumentDocHeader"])
.AddParagraph(string.Format(_localizer["DocumentDocSubHeader"], DateTime.Now))
.AddTable([3000, 3000], tableRows)
.Build();
}
public async Task<Stream> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report OrdersByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataByOrdersAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
var tableRows = data.SelectMany(x =>
(new List<string[]> { new string[] { x.OrderDate.ToShortDateString(), x.TotalCost.ToString("N2"), "", "", "" } })
.Union(x.Services!.Select(y => new string[] { "", "", "", y.ServiceType, y.Count.ToString("N2") }))
).ToList();
tableRows.Insert(0, _tableHeader);
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], tableRows)
.Build();
}
public async Task<Stream> CreateDocumentServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report ServiceByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataByServiceAsync(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(_localizer["DocumentPdfDiagramCaption"], [.. data.Select(x => (x.WorkerFIO, x.TotalAmount))])
.Build();
}
public Task<List<ClientCarDataModel>> GetDataCarsByClientAsync(CancellationToken ct)
{
_logger.LogInformation("Get data CarsByClient");
return GetDataByCarsAsync(ct);
}
public Task<List<OrderDataModel>> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data OrdersByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataByOrdersAsync(dateStart, dateFinish, ct);
}
public Task<List<WorkerServiceByPeriodDataModel>> GetDataServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data ServiceByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataByServiceAsync(dateStart, dateFinish, ct);
}
private async Task<List<ClientCarDataModel>> GetDataByCarsAsync(CancellationToken ct) => [.. (await _carStorageContract.GetListAsync(ct)).GroupBy(x => x.ClientFIO).Select(x => new ClientCarDataModel { ClientFIO = x.Key, Cars = [.. x.Select(y => y.Make)] })];
private async Task<List<OrderDataModel>> GetDataByOrdersAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _orderStorageContract.GetListAsync(dateStart, dateFinish, ct)).OrderBy(x => x.OrderDate)];
}
private async Task<List<WorkerServiceByPeriodDataModel>> GetDataByServiceAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _serviceStorageContract.GetListAsync(dateStart, dateFinish, ct)).GroupBy(x => new { x.WorkerId, x.WorkerFIO }).Select(x => new WorkerServiceByPeriodDataModel { WorkerFIO = x.Key.WorkerFIO, TotalAmount = x.Sum(y => y.ConfigurationModel.Rate), FromPeriod = x.Min(y => y.ServiceDate), ToPeriod = x.Max(y => y.ServiceDate) }).OrderBy(x => x.WorkerFIO)];
}
}

View File

@@ -0,0 +1,134 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure.ServiceConfiguration;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
namespace PimpMyRideBusinessLogic.Implementations;
internal class ServiceBusinessLogicContract(IServiceStorageContracts serviceStorageContracts, IStringLocalizer<Messages> localizer, ILogger logger) : IServiceBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IServiceStorageContracts _serviceStorageContract = serviceStorageContracts;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<ServiceDataModel> GetAllServices()
{
_logger.LogInformation("GetAllServices");
return _serviceStorageContract.GetList();
}
public List<ServiceDataModel> GetAllServicesByWork(string workerId, WorkType workType)
{
_logger.LogInformation("GetAllCars params: {workerId}, {workType}", workerId, workType);
if (workerId.IsEmpty())
{
throw new ArgumentNullException(nameof(workerId));
}
if (!workerId.IsGuid())
{
throw new ValidationException("The value in the field workerId is not an unique identifier.");
}
if (workType == WorkType.None)
{
throw new ValidationException(nameof(workType));
}
return _serviceStorageContract.GetList(workerId, workType);
}
public ServiceDataModel GetServiceByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _serviceStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
throw new ValidationException("The value in the field data is not an unique identifier.");
}
public void InsertService(ServiceDataModel serviceDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(serviceDataModel));
ArgumentNullException.ThrowIfNull(serviceDataModel);
serviceDataModel.Validate(_localizer);
_serviceStorageContract.AddElement(serviceDataModel);
}
public void CancelService(string id)
{
_logger.LogInformation("Cancel by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException("Id is not a unique identifier");
}
_serviceStorageContract.DelElement(id);
}
public double CalculateTotalCost(ServiceDataModel service)
{
_logger.LogInformation("CalculateTotalCost for service {serviceId}", service.Id);
if (service is null)
throw new ArgumentNullException(nameof(service));
service.Validate(_localizer);
var config = service.ConfigurationModel
?? throw new ValidationException("Service configuration is missing");
double laborCost = config.Rate;
switch (service.WorkType)
{
case WorkType.Wheels:
var w = (WheelsServiceConfiguration)config;
if (w.CountOfWheels <= 0)
throw new ValidationException("CountOfWheels must be > 0");
laborCost = w.Rate * w.CountOfWheels;
break;
case WorkType.Engine:
var e = (EngineServiceConfiguration)config;
if (e.EngineComplexity >= 0 || e.LevelOfRepairing >= 0)
{
if (e.EngineComplexity <= 0)
laborCost = e.Rate * e.LevelOfRepairing;
if (e.LevelOfRepairing <= 0)
laborCost = e.Rate * e.EngineComplexity;
else laborCost = e.Rate
* e.EngineComplexity
* e.LevelOfRepairing;
}
break;
}
double materialsCost = service.Materials
.Sum(m => m.Price * m.Count);
double totalCost = laborCost + materialsCost;
_logger.LogDebug("Service {serviceId}: laborCost={laborCost}, materialsCost={materialsCost}, totalCost={totalCost}",
service.Id, laborCost, materialsCost, totalCost);
return totalCost;
}
}

View File

@@ -0,0 +1,100 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using PimpMyRideContracts.BusinessLogicsContracts;
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.StoragesContracts;
using System.Text.Json;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace PimpMyRideBusinessLogic.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> 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 List<WorkerDataModel> GetAllWorkersBySpeciality(SpecialityType specialityType, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {specialityType}, {onlyActive},", specialityType, onlyActive);
if (specialityType == SpecialityType.None)
{
throw new ArgumentNullException(nameof(specialityType));
}
return _workerStorageContract.GetList(onlyActive, specialityType);
}
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("Id is not a unique identifier");
}
_workerStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideBusinessLogic.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 PimpMyRideBusinessLogic.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 PimpMyRideBusinessLogic.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 PimpMyRideBusinessLogic.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.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
namespace PimpMyRideBusinessLogic.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 PimpMyRideBusinessLogic.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,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="PimpMyRideTest" />
<InternalsVisibleTo Include="PimpMyRideWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.2" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PimpMyRideContracts\PimpMyRideContracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
namespace PimpMyRideContracts.AdapterContracts;
public interface ICarAdapter
{
CarOperationResponse GetList();
CarOperationResponse GetClientList(string id);
CarOperationResponse GetMakeList(string make, string model);
CarOperationResponse GetElement(string data);
CarOperationResponse RegisterCar(CarBindingModel buyerModel);
CarOperationResponse ChangeCarInfo(CarBindingModel buyerModel);
CarOperationResponse RemoveCar(string id);
}

View File

@@ -0,0 +1,13 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
namespace PimpMyRideContracts.AdapterContracts;
public interface IClientAdapter
{
ClientOperationResponse GetList();
ClientOperationResponse GetElement(string data);
ClientOperationResponse RegisterClient(ClientBindingModel buyerModel);
ClientOperationResponse ChangeClientInfo(ClientBindingModel buyerModel);
ClientOperationResponse RemoveClient(string id);
}

View File

@@ -0,0 +1,22 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.AdapterContracts;
public interface IMaterialAdapter
{
MaterialOperationResponse GetList();
MaterialOperationResponse GetTypeList(MaterialType materialType);
MaterialOperationResponse GetHistory(string id);
MaterialOperationResponse GetElement(string data);
MaterialOperationResponse RegisterMaterial(MaterialBindingModel materialModel);
MaterialOperationResponse ChangeMaterialInfo(MaterialBindingModel materialModel);
MaterialOperationResponse RemoveMaterial(string id);
}

View File

@@ -0,0 +1,18 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.AdapterContracts;
public interface IOrderAdapter
{
OrderOperationResponse GetList(DateTime fromDate, DateTime toDate);
OrderOperationResponse GetCarList(string id, DateTime fromDate, DateTime toDate);
OrderOperationResponse GetElement(string data);
OrderOperationResponse MakeOrder(OrderBindingModel saleModel);
OrderOperationResponse CancelOrder(string id);
}

View File

@@ -0,0 +1,19 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataCarsByClientAsync(CancellationToken ct);
Task<ReportOperationResponse> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentCarsByClientAsync(CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,18 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.AdapterContracts;
public interface IServiceAdapter
{
ServiceOperationResponse GetList();
ServiceOperationResponse GetWorkerList(string id, WorkType workType);
ServiceOperationResponse GetElement(string data);
ServiceOperationResponse MakeService(ServiceBindingModel saleModel);
ServiceOperationResponse CancelService(string id);
}

View File

@@ -0,0 +1,24 @@
using PimpMyRideContracts.AdapterContracts.OperationResponses;
using PimpMyRideContracts.BindingModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.AdapterContracts;
public interface IWorkerAdapter
{
WorkerOperationResponse GetList(bool includeDeleted);
WorkerOperationResponse GetSpecialityTypeList(SpecialityType specialityType, 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,14 @@
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.ViewModels;
namespace PimpMyRideContracts.AdapterContracts.OperationResponses;
public class CarOperationResponse : OperationResponse
{
public static CarOperationResponse OK(List<CarViewModel> data) => OK<CarOperationResponse, List<CarViewModel>>(data);
public static CarOperationResponse OK(CarViewModel data) => OK<CarOperationResponse, CarViewModel>(data);
public static CarOperationResponse NoContent() => NoContent<CarOperationResponse>();
public static CarOperationResponse NotFound(string message) => NotFound<CarOperationResponse>(message);
public static CarOperationResponse BadRequest(string message) => BadRequest<CarOperationResponse>(message);
public static CarOperationResponse InternalServerError(string message) => InternalServerError<CarOperationResponse>(message);
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,20 @@
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.ViewModels;
namespace PimpMyRideContracts.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ClientCarViewModel> data) => OK<ReportOperationResponse, List<ClientCarViewModel>>(data);
public static ReportOperationResponse OK(List<OrderViewModel> data) => OK<ReportOperationResponse, List<OrderViewModel>>(data);
public static ReportOperationResponse OK(List<WorkerServiceByPeriodViewModel> data) => OK<ReportOperationResponse, List<WorkerServiceByPeriodViewModel>>(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 PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.ViewModels;
namespace PimpMyRideContracts.AdapterContracts.OperationResponses;
public class ServiceOperationResponse : OperationResponse
{
public static ServiceOperationResponse OK(List<ServiceViewModel> data) => OK<ServiceOperationResponse, List<ServiceViewModel>>(data);
public static ServiceOperationResponse OK(ServiceViewModel data) => OK<ServiceOperationResponse, ServiceViewModel>(data);
public static ServiceOperationResponse NoContent() => NoContent<ServiceOperationResponse>();
public static ServiceOperationResponse NotFound(string message) => NotFound<ServiceOperationResponse>(message);
public static ServiceOperationResponse BadRequest(string message) => BadRequest<ServiceOperationResponse>(message);
public static ServiceOperationResponse InternalServerError(string message) => InternalServerError<ServiceOperationResponse>(message);
}

View File

@@ -0,0 +1,19 @@
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.ViewModels;
namespace PimpMyRideContracts.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,14 @@
namespace PimpMyRideContracts.BindingModels;
public class CarBindingModel
{
public string? Id { get; set; }
public string? ClientId { get; set; }
public string? Make { get; set; }
public string? Model { get; set; }
public string? StateNumber { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.BindingModels;
public class ClientBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,14 @@
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BindingModels;
public class MaterialBindingModel
{
public string? Id { get; set; }
public string? MaterialType { get; set; }
public string? Description { get; set; }
public double UnitCost { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.BindingModels;
public class OrderBindingModel
{
public string? Id { get; set; }
public string? CarId { get; set; }
public List<OrderServiceBindingModel>? Services { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideContracts.BindingModels;
public class OrderServiceBindingModel
{
public string? OrderId { get; set; }
public string? ServiceId { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,16 @@
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BindingModels;
public class ServiceBindingModel
{
public string? Id { get; set; }
public string? WorkerId { get; set; }
public WorkType WorkType { get; set; }
public List<ServiceMaterialBindingModel>? Materials { get; set; }
public string? ConfigurationJson { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideContracts.BindingModels;
public class ServiceMaterialBindingModel
{
public string? ServiceId { get; set; }
public string? MaterialId { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,16 @@
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BindingModels;
public class WorkerBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public SpecialityType SpecialityType { get; set; }
public DateTime BirthDate { get; set; }
public DateTime EmploymentDate { get; set; }
}

View File

@@ -0,0 +1,20 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface ICarBusinessLogicContract
{
List<CarDataModel> GetAllCars();
List<CarDataModel> GetAllCarsByClient(string clientId);
List<CarDataModel> GetAllCarsByMake(string make, string model);
CarDataModel GetCarByData(string data);
void InsertCar(CarDataModel carDataModel);
void UpdateCar(CarDataModel carDataModel);
void DeleteCar(string id);
}

View File

@@ -0,0 +1,15 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IClientBusinessLogicContract
{
List<ClientDataModel> GetAllClients();
ClientDataModel GetClientByData(string data);
void InsertClient(ClientDataModel clientDataModel);
void UpdateClient(ClientDataModel clientDataModel);
void DeleteClient(string id);
}

View File

@@ -0,0 +1,21 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IMaterialBusinessLogicContract
{
List<MaterialDataModel> GetAllMaterials();
List<MaterialDataModel> GetAllMaterialsByType(MaterialType materialType);
List<MaterialHistoryDataModel> GetMaterialHistoryById(string materialId);
MaterialDataModel GetMaterialByData(string data);
void InsertMaterial(MaterialDataModel materialDataModel);
void UpdateMaterial(MaterialDataModel materialDataModel);
void DeleteMaterial(string id);
}

View File

@@ -0,0 +1,16 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IOrderBusinessLogicContract
{
List<OrderDataModel> GetAllOrdersByPeriod(DateTime fromDate, DateTime toDate);
List<OrderDataModel> GetAllOrdersByCarByPeriod(string carId, DateTime fromDate, DateTime toDate);
OrderDataModel GetOrderByData(string data);
void InsertOrder(OrderDataModel orderDataModel);
void CancelOrder(string id);
}

View File

@@ -0,0 +1,18 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IReportContract
{
Task<List<ClientCarDataModel>> GetDataCarsByClientAsync(CancellationToken ct);
Task<List<OrderDataModel>> GetDataOrderByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<WorkerServiceByPeriodDataModel>> GetDataServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentCarsByClientAsync(CancellationToken ct);
Task<Stream> CreateDocumentOrdersByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentServiceByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,19 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IServiceBusinessLogicContract
{
List<ServiceDataModel> GetAllServices();
List<ServiceDataModel> GetAllServicesByWork(string workerId, WorkType workType);
ServiceDataModel GetServiceByData(string data);
void InsertService(ServiceDataModel serviceDataModel);
void CancelService(string id);
double CalculateTotalCost(ServiceDataModel serviceDataModel);
}

View File

@@ -0,0 +1,23 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.BusinessLogicsContracts;
internal interface IWorkerBusinessLogicContract
{
List<WorkerDataModel> GetAllWorkers(bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersBySpeciality(SpecialityType specialityType, bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersByBirthDate(DateTime fromBirthDate, DateTime toBirthDate, bool onlyActive = true);
List<WorkerDataModel> GetAllWorkersByEmploymentDate(DateTime fromEmploymentDate, DateTime toEmploymentDate, bool onlyActive = true);
WorkerDataModel GetWorkerByData(string data);
void InsertWorker(WorkerDataModel workerDataModel);
void UpdateWorker(WorkerDataModel workerDataModel);
void DeleteWorker(string id);
}

View File

@@ -0,0 +1,57 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class CarDataModel(string id, string clientId, string make, string model, string stateNumber) : IValidation
{
[SourceFromMember(SourcePropertyName = "Client")]
private readonly ClientDataModel? _client;
public string Id { get; private set; } = id;
public string ClientId { get; private set; } = clientId;
public string Make { get; private set; } = make;
public string Model { get; private set; } = model;
public string StateNumber { get; private set; } = stateNumber;
public string ClientFIO => _client?.FIO ?? string.Empty;
public CarDataModel(string id, string clientId, string make, string model, string stateNumber, ClientDataModel client) : this(id, clientId, make, model, stateNumber)
{
_client = client;
}
public CarDataModel() : this(string.Empty, string.Empty, string.Empty, string.Empty, string.Empty) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (ClientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ClientId"));
if (!ClientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "ClientId"));
if (Make.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Make"));
if (Model.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Model"));
if (StateNumber.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "StateNumber"));
}
}

View File

@@ -0,0 +1,8 @@
namespace PimpMyRideContracts.DataModels;
public class ClientCarDataModel
{
public required string ClientFIO { get; set; }
public required List<string> Cars { get; set; }
}

View File

@@ -0,0 +1,37 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Resources;
using System.Text.RegularExpressions;
namespace PimpMyRideContracts.DataModels;
internal class ClientDataModel(string id, string fio, string phoneNumber) : IValidation
{
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string PhoneNumber { get; private set; } = phoneNumber;
public ClientDataModel() : this(string.Empty, string.Empty, string.Empty) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (FIO.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "FIO"));
if (PhoneNumber.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PhoneNumber"));
if (!Regex.IsMatch(PhoneNumber, @"^((\+7|7|8)+([0-9]){10})$"))
throw new ValidationException(localizer["ValidationExceptionMessageIncorrectPhoneNumber"]);
}
}

View File

@@ -0,0 +1,39 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class MaterialDataModel(string id, MaterialType materialType, string description, double unitCost) : IValidation
{
public string Id { get; private set; } = id;
public MaterialType MaterialType { get; private set; } = materialType;
public string Description { get; private set; } = description;
public double UnitCost { get; private set; } = unitCost;
public MaterialDataModel() : this(string.Empty, MaterialType.None, string.Empty, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (MaterialType == MaterialType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Materialtype"));
if (Description.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Description"));
if (UnitCost <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "UnitCost"));
}
}

View File

@@ -0,0 +1,42 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class MaterialHistoryDataModel(string materialId, double oldPrice) : IValidation
{
[SourceFromMember(SourcePropertyName = "Material")]
private readonly MaterialDataModel? _material;
public string MaterialId { get; private set; } = materialId;
public double OldPrice { get; private set; } = oldPrice;
public DateTime ChangeDate { get; private set;} = DateTime.UtcNow;
public string MaterialDescription => _material?.Description ?? string.Empty;
public MaterialHistoryDataModel(string materialId, double oldPrice, DateTime changeDate, MaterialDataModel material) : this(materialId, oldPrice)
{
ChangeDate = changeDate;
_material = material;
}
public MaterialHistoryDataModel() : this(string.Empty, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (MaterialId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "MaterialId"));
if (!MaterialId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "MaterialId"));
if (OldPrice <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "OldPrice"));
}
}

View File

@@ -0,0 +1,72 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class OrderDataModel : IValidation
{
[SourceFromMember(SourcePropertyName = "Car")]
private readonly CarDataModel? _car;
public string Id { get; private set; }
public string CarId { get; private set; }
public DateTime OrderDate { get; private set; } = DateTime.UtcNow;
public double TotalCost { get; private set; }
public bool IsCancel { get; private set; }
[AlternativeName("OrderServices")]
public List<OrderServiceDataModel> Services { get; private set; }
public string CarMake => _car?.Make ?? string.Empty;
public OrderDataModel(string id, string carId, bool isCancel, List<OrderServiceDataModel> orderServices)
{
Id = id;
CarId = carId;
IsCancel = isCancel;
Services = orderServices;
TotalCost = 10;
}
public OrderDataModel(string id, string carId, double totalCost, bool isCancel, List<OrderServiceDataModel> orderServices, CarDataModel car) : this(id, carId, isCancel, orderServices)
{
TotalCost = totalCost;
_car = car;
}
public OrderDataModel() : this(string.Empty, string.Empty, false, new List<OrderServiceDataModel>()) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (CarId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "CarId"));
if (!CarId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "CarId"));
if (TotalCost <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "TotalCost"));
if ((Services?.Count ?? 0) == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoServicesInOrder"]);
foreach (var service in Services)
{
service.Validate(localizer);
}
}
}

View File

@@ -0,0 +1,53 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class OrderServiceDataModel(string orderId, string serviceId, int count, double price) : IValidation
{
[SourceFromMember(SourcePropertyName = "Service")]
private readonly ServiceDataModel? _service;
public string OrderId { get; private set; } = orderId;
public string ServiceId { get; private set; } = serviceId;
public int Count { get; private set; } = count;
public double Price { get; private set; } = price;
public string ServiceType => _service?.WorkType.ToString() ?? WorkType.None.ToString();
public OrderServiceDataModel(string orderId, string serviceId, int count, double price, ServiceDataModel service) : this(orderId, serviceId, count, price)
{
_service = service;
}
public OrderServiceDataModel() : this(string.Empty, string.Empty, 1, 10) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (OrderId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "OrderId"));
if (!OrderId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "OrderId"));
if (ServiceId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ServiceId"));
if (!ServiceId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "ServiceId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if (Price <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

View File

@@ -0,0 +1,118 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Infrastructure.ServiceConfiguration;
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Resources;
using PimpMyRideContracts.Mapper;
namespace PimpMyRideContracts.DataModels;
internal class ServiceDataModel : IValidation
{
[SourceFromMember(SourcePropertyName = "Worker")]
private readonly WorkerDataModel? _worker;
public string Id { get; private set; }
public string WorkerId { get; private set; }
public WorkType WorkType { get; private set; }
[AlternativeName("Configuration")]
[AlternativeName("ConfigurationJson")]
[PostProcessing(MappingCallMethodName = "ParseJson")]
public ServiceConfiguration ConfigurationModel { get; private set; }
[AlternativeName("ServiceMaterials")]
public List<ServiceMaterialDataModel> Materials { get; private set; }
[IgnoreValue]
public DateTime ServiceDate { get; private set; } = DateTime.UtcNow;
public string WorkerFIO => _worker?.FIO ?? string.Empty;
public ServiceDataModel(string id, string workerId, WorkType workType, ServiceConfiguration configurationModel, List<ServiceMaterialDataModel> serviceMaterials)
{
Id = id;
WorkerId = workerId;
WorkType = workType;
ConfigurationModel = configurationModel;
Materials = serviceMaterials;
}
public ServiceDataModel(string id, string workerId, WorkType workType, ServiceConfiguration configurationModel, List<ServiceMaterialDataModel> serviceMaterials, DateTime serviceDate)
{
Id = id;
WorkerId = workerId;
WorkType = workType;
ConfigurationModel = configurationModel;
Materials = serviceMaterials;
ServiceDate = serviceDate;
}
public ServiceDataModel(string id, string workerId, WorkType workType, ServiceConfiguration configurationModel, List<ServiceMaterialDataModel> serviceMaterials, WorkerDataModel worker, DateTime serviceDate) : this(id, workerId, workType, configurationModel, serviceMaterials, serviceDate)
{
_worker = worker;
}
public ServiceDataModel(string id, string workerId, WorkType workType, ServiceConfiguration configurationModel, List<ServiceMaterialDataModel> serviceMaterials, WorkerDataModel worker) : this(id, workerId, workType, configurationModel, serviceMaterials)
{
_worker = worker;
}
public ServiceDataModel(string id, string workerId, int workType, ServiceConfiguration configurationModel, List<ServiceMaterialDataModel> materials) : this(id, workerId, (WorkType)workType, configurationModel, materials) { }
public ServiceDataModel() : this(string.Empty, string.Empty, WorkType.None, (ServiceConfiguration)null, new List<ServiceMaterialDataModel>()) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (WorkerId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkerId"));
if (!WorkerId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "WorkerId"));
if (WorkType == WorkType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkType"));
if (ConfigurationModel is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "ConfigurationModel"));
if (ConfigurationModel!.Rate <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Rate"));
if ((Materials?.Count ?? 0) == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoMaterialsInService"]);
}
private ServiceConfiguration? ParseJson(string json)
{
if (string.IsNullOrWhiteSpace(json)) return null;
var obj = JToken.Parse(json);
var type = obj.Value<string>("Type");
switch (type)
{
case nameof(EngineServiceConfiguration):
ConfigurationModel = JsonConvert.DeserializeObject<EngineServiceConfiguration>(json);
break;
case nameof(WheelsServiceConfiguration):
ConfigurationModel = JsonConvert.DeserializeObject<WheelsServiceConfiguration>(json);
break;
default:
ConfigurationModel = JsonConvert.DeserializeObject<ServiceConfiguration>(json);
break;
}
return ConfigurationModel;
}
}

View File

@@ -0,0 +1,52 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class ServiceMaterialDataModel(string serviceId, string materialId, int count, double price) : IValidation
{
[SourceFromMember(SourcePropertyName = "Material")]
private readonly MaterialDataModel? _material;
public string ServiceId { get; private set; } = serviceId;
public string MaterialId { get; private set; } = materialId;
public int Count { get; private set; } = count;
public double Price { get; private set; } = price;
public string MaterialName => _material?.Description ?? string.Empty;
public ServiceMaterialDataModel(string serviceId, string materialId, int count, double price, MaterialDataModel material) : this(serviceId, materialId, count, price)
{
_material = material;
}
public ServiceMaterialDataModel() : this(string.Empty, string.Empty, 0, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (ServiceId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ServiceId"));
if (!ServiceId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "ServiceId"));
if (MaterialId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "MaterialId"));
if (!MaterialId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "MaterialId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if (Price <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

View File

@@ -0,0 +1,57 @@
using Microsoft.Extensions.Localization;
using PimpMyRideContracts.Enums;
using PimpMyRideContracts.Exceptions;
using PimpMyRideContracts.Extensions;
using PimpMyRideContracts.Infrastructure;
using PimpMyRideContracts.Mapper;
using PimpMyRideContracts.Resources;
namespace PimpMyRideContracts.DataModels;
internal class WorkerDataModel(string id, string fio, SpecialityType specialityType, DateTime birthDate, DateTime employmentDate, bool isDeleted) : IValidation
{
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public SpecialityType SpecialityType { get; private set; } = specialityType;
[PostProcessing(ActionType = PostProcessingType.ToUniversalTime)]
public DateTime BirthDate { get; private set; } = birthDate.ToUniversalTime();
[PostProcessing(ActionType = PostProcessingType.ToUniversalTime)]
public DateTime EmploymentDate { get; private set; } = employmentDate.ToUniversalTime();
[CustomDefault(DefaultValue = false)]
public bool IsDeleted { get; private set; } = isDeleted;
public WorkerDataModel(string id, string fio, SpecialityType specialityType, DateTime birthDate, DateTime employmentDate) : this(id, fio, specialityType, birthDate, employmentDate, false) { }
public WorkerDataModel() : this(string.Empty, string.Empty, SpecialityType.None, DateTime.UtcNow, DateTime.UtcNow, false) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Id"));
if (!Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAnId"], "Id"));
if (FIO.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "FIO"));
if (SpecialityType == SpecialityType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SpecialityType"));
if (BirthDate.Date > DateTime.Now.AddYears(-18).Date)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsBirthDate"], BirthDate.ToShortDateString()));
if (EmploymentDate.Date < BirthDate.Date)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmploymentDateAndBirthDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
if ((EmploymentDate - BirthDate).TotalDays / 365 < 16) // EmploymentDate.Year - BirthDate.Year
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsEmploymentDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
}
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideContracts.DataModels;
public class WorkerServiceByPeriodDataModel
{
public required string WorkerFIO { get; set; }
public double TotalAmount { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.Enums;
public enum MaterialType
{
None = 0,
SmallConsumables = 1,
BodyParts = 2,
EngineParts = 3,
Wheels = 4
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.Enums;
public enum SpecialityType
{
None = 0,
TireFitter = 1,
BodyWorker = 2,
MaintenanceWorker = 3,
Mechanic = 4
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.Enums;
public enum WorkType
{
None = 0,
Wheels = 1,
Body = 2,
Maintenance = 3,
Engine = 4
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
namespace PimpMyRideContracts.Extensions;
public static class StringExtensions
{
public static bool IsEmpty(this string str)
{
return string.IsNullOrWhiteSpace(str);
}
public static bool IsGuid(this string str)
{
return Guid.TryParse(str, out _);
}
}

View File

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

View File

@@ -0,0 +1,6 @@
namespace PimpMyRideContracts.Infrastructure;
public interface IConfigurationOrder
{
public int MaxThreads { get; }
}

View File

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

View File

@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Net;
namespace PimpMyRideContracts.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/octetstream")
{
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,12 @@
using System.Transactions;
namespace PimpMyRideContracts.Infrastructure.ServiceConfiguration;
public class EngineServiceConfiguration : ServiceConfiguration
{
public override string Type => nameof(EngineServiceConfiguration);
public double EngineComplexity { get; set; }
public double LevelOfRepairing { get; set; }
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace PimpMyRideContracts.Infrastructure.ServiceConfiguration;
public class WheelsServiceConfiguration : ServiceConfiguration
{
public override string Type => nameof(WheelsServiceConfiguration);
public double CountOfWheels { get; set; }
}

View File

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

View File

@@ -0,0 +1,9 @@
namespace PimpMyRideContracts.Mapper;
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
class CustomDefaultAttribute : Attribute
{
public object? DefaultValue { get; set; }
public DefaultValuesFunc FuncName { get; set; }
}

View File

@@ -0,0 +1,216 @@
using System.Collections;
using System.ComponentModel;
using System.Reflection;
namespace PimpMyRideContracts.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
if(property.GetCustomAttribute(typeof(IgnoreValueAttribute)) != null)
{
FindAndMapDefaultValue(property, newObject);
continue;
}
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) && propertyFrom.PropertyType != property.PropertyType)
{
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)
{
object? finalValue = null;
bool successConversion = false;
var targetType = property.PropertyType;
if (propertyFrom.PropertyType.IsEnum && !targetType.IsEnum)
{
finalValue = fromValue.ToString();
successConversion = true;
}
else if (targetType.IsEnum)
{
successConversion = Enum.TryParse(targetType, fromValue.ToString(), ignoreCase: true, out object? parsedEnum);
if (successConversion)
{
finalValue = parsedEnum;
}
}
else
{
finalValue = fromValue;
successConversion = true;
}
if (successConversion)
{
property.SetValue(newObject, finalValue);
}
}
}
// fields
var fieldsTo = typeTo.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fieldsTo)
{
var mapFromAttr = field.GetCustomAttribute<SourceFromMemberAttribute>();
if (mapFromAttr is null)
continue;
var sourceProp = typeFrom.GetProperty(mapFromAttr.SourcePropertyName);
if (sourceProp is null || !sourceProp.CanRead)
continue;
var sourceValue = sourceProp.GetValue(obj);
if (sourceValue is null)
continue;
var targetFieldType = field.FieldType;
var mappedValue = typeof(CustomMapper)
.GetMethod(nameof(MapObject), new[] { typeof(object) })!
.MakeGenericMethod(targetFieldType)
.Invoke(null, new[] { sourceValue });
if (mappedValue is not null)
{
field.SetValue(newObject, mappedValue);
}
}
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<CustomDefaultAttribute>();
if (defaultValueAttribute is null)
{
return;
}
if (defaultValueAttribute.DefaultValue is not null)
{
property.SetValue(newObject, defaultValueAttribute.DefaultValue);
return;
}
var value = defaultValueAttribute.FuncName switch
{
DefaultValuesFunc.UtcNow => DateTime.UtcNow,
DefaultValuesFunc.Null => (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 PimpMyRideContracts.Mapper
{
enum DefaultValuesFunc
{
Null,
UtcNow
}
}

View File

@@ -0,0 +1,8 @@

namespace PimpMyRideContracts.Mapper
{
[AttributeUsage(AttributeTargets.Property)]
class IgnoreValueAttribute : Attribute
{
}
}

View File

@@ -0,0 +1,10 @@

namespace PimpMyRideContracts.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 PimpMyRideContracts.Mapper
{
enum PostProcessingType
{
None = -1,
ToUniversalTime = 1,
ToLocalTime = 2,
}
}

View File

@@ -0,0 +1,9 @@

namespace PimpMyRideContracts.Mapper
{
[AttributeUsage(AttributeTargets.Field)]
class SourceFromMemberAttribute : Attribute
{
public string? SourcePropertyName { get; set; }
}
}

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.5" />
</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="PimpMyRideBusinessLogic" />
<InternalsVisibleTo Include="PimpMyRideDatabase" />
<InternalsVisibleTo Include="PimpMyRideWebApi" />
<InternalsVisibleTo Include="PimpMyRideTest" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,387 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Этот код создан программой.
// Исполняемая версия:4.0.30319.42000
//
// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае
// повторной генерации кода.
// </auto-generated>
//------------------------------------------------------------------------------
namespace PimpMyRideContracts.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("PimpMyRideContracts.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 DocumentDocCaptionCar {
get {
return ResourceManager.GetString("DocumentDocCaptionCar", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Клиент.
/// </summary>
internal static string DocumentDocCaptionClient {
get {
return ResourceManager.GetString("DocumentDocCaptionClient", 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 DocumentExcelCaptionMaterial {
get {
return ResourceManager.GetString("DocumentExcelCaptionMaterial", 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 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 ValidationExceptionMessageNoMaterialsInService {
get {
return ResourceManager.GetString("ValidationExceptionMessageNoMaterialsInService", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на В заказе должна быть хотя бы одна услуга.
/// </summary>
internal static string ValidationExceptionMessageNoServicesInOrder {
get {
return ResourceManager.GetString("ValidationExceptionMessageNoServicesInOrder", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} не является типом уникального идентификатора.
/// </summary>
internal static string ValidationExceptionMessageNotAnId {
get {
return ResourceManager.GetString("ValidationExceptionMessageNotAnId", resourceCulture);
}
}
/// <summary>
/// Ищет локализованную строку, похожую на Значение в поле {0} не проиницализировано.
/// </summary>
internal static string ValidationExceptionMessageNotInitialized {
get {
return ResourceManager.GetString("ValidationExceptionMessageNotInitialized", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,228 @@
<?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="AdapterMessageElementDeletedException" xml:space="preserve">
<value>Element von Daten: {0} wurde gelöscht</value>
</data>
<data name="AdapterMessageElementNotFoundException" xml:space="preserve">
<value>Nicht gefundenes Element durch Daten: {0}</value>
</data>
<data name="AdapterMessageEmptyDate" xml:space="preserve">
<value>Die Daten sind leer</value>
</data>
<data name="AdapterMessageIncorrectDatesException" xml:space="preserve">
<value>Falsche Daten: {0}</value>
</data>
<data name="AdapterMessageInvalidOperationException" xml:space="preserve">
<value>Fehler bei der Verarbeitung der Daten: {0}</value>
</data>
<data name="AdapterMessageStorageException" xml:space="preserve">
<value>Fehler beim Arbeiten mit dem Datenspeicher: {0}</value>
</data>
<data name="AdapterMessageValidationException" xml:space="preserve">
<value>Falsche Daten übermittelt: {0}</value>
</data>
<data name="DocumentDocCaptionCar" xml:space="preserve">
<value>Auto</value>
</data>
<data name="DocumentDocCaptionClient" xml:space="preserve">
<value>Kunde</value>
</data>
<data name="DocumentDocHeader" xml:space="preserve">
<value>Autos vom Kunden</value>
</data>
<data name="DocumentDocSubHeader" xml:space="preserve">
<value>Generiert am Datum {0}</value>
</data>
<data name="DocumentExcelCaptionCount" xml:space="preserve">
<value>Zählen</value>
</data>
<data name="DocumentExcelCaptionDate" xml:space="preserve">
<value>Datum</value>
</data>
<data name="DocumentExcelCaptionMaterial" xml:space="preserve">
<value>Material</value>
</data>
<data name="DocumentExcelCaptionSum" xml:space="preserve">
<value>Summe</value>
</data>
<data name="DocumentExcelCaptionTotal" xml:space="preserve">
<value>Gesamt</value>
</data>
<data name="DocumentExcelHeader" xml:space="preserve">
<value>Bestellungen für den Zeitraum</value>
</data>
<data name="DocumentExcelSubHeader" xml:space="preserve">
<value>von {0} bis {1}</value>
</data>
<data name="DocumentPdfDiagramCaption" xml:space="preserve">
<value>Dienstleistungen</value>
</data>
<data name="DocumentPdfHeader" xml:space="preserve">
<value>Leistungserklärung</value>
</data>
<data name="DocumentPdfSubHeader" xml:space="preserve">
<value>für den Zeitraum vom {0} bis {1}</value>
</data>
<data name="ElementDeletedExceptionMessage" xml:space="preserve">
<value>Ein gelöschtes Element (ID: {0}) kann nicht geändert werden.</value>
</data>
<data name="ElementExistsExceptionMessage" xml:space="preserve">
<value>Es gibt bereits ein Element mit dem Wert {0} des Parameters {1}</value>
</data>
<data name="ElementNotFoundExceptionMessage" xml:space="preserve">
<value>Element bei Wert = {0} nicht gefunden</value>
</data>
<data name="IncorrectDatesExceptionMessage" xml:space="preserve">
<value>Das Enddatum muss nach dem Startdatum liegen. Startdatum: {0}. Enddatum: {1}</value>
</data>
<data name="StorageExceptionMessage" xml:space="preserve">
<value>Fehler beim Arbeiten im Speicher: {0}</value>
</data>
<data name="ValidationExceptionMessageEmploymentDateAndBirthDate" xml:space="preserve">
<value>Das Beschäftigungsdatum darf nicht vor dem Geburtsdatum liegen ({0}, {1})</value>
</data>
<data name="ValidationExceptionMessageEmptyField" xml:space="preserve">
<value>Der Wert im Feld {0} ist leer</value>
</data>
<data name="ValidationExceptionMessageIncorrectPhoneNumber" xml:space="preserve">
<value>Der Wert im Feld „Telefonnummer“ ist keine Telefonnummer.</value>
</data>
<data name="ValidationExceptionMessageLessOrEqualZero" xml:space="preserve">
<value>Der Wert im Feld {0} ist kleiner oder gleich 0</value>
</data>
<data name="ValidationExceptionMessageMinorsBirthDate" xml:space="preserve">
<value>Minderjährige können nicht eingestellt werden (Geburtsdatum = {0})</value>
</data>
<data name="ValidationExceptionMessageMinorsEmploymentDate" xml:space="preserve">
<value>Minderjährige können nicht eingestellt werden (Beschäftigungsdatum: {0}, Geburtsdatum: {1})</value>
</data>
<data name="ValidationExceptionMessageNoMaterialsInService" xml:space="preserve">
<value>Es muss mindestens ein Artikel im Service vorhanden sein</value>
</data>
<data name="ValidationExceptionMessageNoServicesInOrder" xml:space="preserve">
<value>Es muss mindestens ein Artikel bestellt werden</value>
</data>
<data name="ValidationExceptionMessageNotAnId" xml:space="preserve">
<value>Der Wert im Feld {0} ist kein eindeutiger Kennungstyp.</value>
</data>
<data name="ValidationExceptionMessageNotInitialized" xml:space="preserve">
<value>Der Wert im Feld {0} ist nicht initialisiert</value>
</data>
</root>

View File

@@ -0,0 +1,228 @@
<?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="AdapterMessageElementDeletedException" xml:space="preserve">
<value>Element by data: {0} was deleted</value>
</data>
<data name="AdapterMessageElementNotFoundException" xml:space="preserve">
<value>Not found element by data: {0}</value>
</data>
<data name="AdapterMessageEmptyDate" xml:space="preserve">
<value>Data is empty</value>
</data>
<data name="AdapterMessageIncorrectDatesException" xml:space="preserve">
<value>Incorrect dates: {0}</value>
</data>
<data name="AdapterMessageInvalidOperationException" xml:space="preserve">
<value>Error processing data: {0}</value>
</data>
<data name="AdapterMessageStorageException" xml:space="preserve">
<value>Error while working with data storage: {0}</value>
</data>
<data name="AdapterMessageValidationException" xml:space="preserve">
<value>Incorrect data transmitted: {0}</value>
</data>
<data name="DocumentDocCaptionCar" xml:space="preserve">
<value>Car</value>
</data>
<data name="DocumentDocCaptionClient" xml:space="preserve">
<value>Client</value>
</data>
<data name="DocumentDocHeader" xml:space="preserve">
<value>Cars by Client</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="DocumentExcelCaptionMaterial" xml:space="preserve">
<value>Material</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>Orders for the period</value>
</data>
<data name="DocumentExcelSubHeader" xml:space="preserve">
<value>from {0} to {1}</value>
</data>
<data name="DocumentPdfDiagramCaption" xml:space="preserve">
<value>Services</value>
</data>
<data name="DocumentPdfHeader" xml:space="preserve">
<value>Services statement</value>
</data>
<data name="DocumentPdfSubHeader" xml:space="preserve">
<value>for the period from {0} to {1}</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="StorageExceptionMessage" xml:space="preserve">
<value>Error while working in storage: {0}</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="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="ValidationExceptionMessageLessOrEqualZero" xml:space="preserve">
<value>The value in field {0} is less than or equal to 0</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="ValidationExceptionMessageNoMaterialsInService" xml:space="preserve">
<value>There must be at least one item on service</value>
</data>
<data name="ValidationExceptionMessageNoServicesInOrder" xml:space="preserve">
<value>There must be at least one item on order</value>
</data>
<data name="ValidationExceptionMessageNotAnId" xml:space="preserve">
<value>The value in the {0} field is not a unique identifier type.</value>
</data>
<data name="ValidationExceptionMessageNotInitialized" xml:space="preserve">
<value>The value in field {0} is not initialized</value>
</data>
</root>

View File

@@ -0,0 +1,228 @@
<?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="AdapterMessageElementDeletedException" xml:space="preserve">
<value>Элемент по данным: {0} был удален</value>
</data>
<data name="AdapterMessageElementNotFoundException" xml:space="preserve">
<value>Не найден элемент по данным: {0}</value>
</data>
<data name="AdapterMessageEmptyDate" xml:space="preserve">
<value>Данные пусты</value>
</data>
<data name="AdapterMessageIncorrectDatesException" xml:space="preserve">
<value>Неправильные даты: {0}</value>
</data>
<data name="AdapterMessageInvalidOperationException" xml:space="preserve">
<value>Ошибка при обработке данных: {0}</value>
</data>
<data name="AdapterMessageStorageException" xml:space="preserve">
<value>Ошибка при работе с хранилищем данных: {0}</value>
</data>
<data name="AdapterMessageValidationException" xml:space="preserve">
<value>Переданы неверные данные: {0}</value>
</data>
<data name="DocumentDocCaptionCar" xml:space="preserve">
<value>Автомобиль</value>
</data>
<data name="DocumentDocCaptionClient" 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="DocumentExcelCaptionMaterial" 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="DocumentPdfDiagramCaption" xml:space="preserve">
<value>Начисления</value>
</data>
<data name="DocumentPdfHeader" xml:space="preserve">
<value>Ведомость по заказам</value>
</data>
<data name="DocumentPdfSubHeader" xml:space="preserve">
<value>за период с {0} по {1}</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="StorageExceptionMessage" xml:space="preserve">
<value>Ошибка при работе в хранилище: {0}</value>
</data>
<data name="ValidationExceptionMessageEmploymentDateAndBirthDate" xml:space="preserve">
<value>Дата трудоустройства не может быть раньше даты рождения ({0}, {1})</value>
</data>
<data name="ValidationExceptionMessageEmptyField" xml:space="preserve">
<value>Значение в поле {0} пусто</value>
</data>
<data name="ValidationExceptionMessageIncorrectPhoneNumber" xml:space="preserve">
<value>Значение в поле Телефонный номер не является телефонным номером</value>
</data>
<data name="ValidationExceptionMessageLessOrEqualZero" xml:space="preserve">
<value>Значение в поле {0} меньше или равно 0</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="ValidationExceptionMessageNoMaterialsInService" xml:space="preserve">
<value>В услуге должен быть хотя бы один товар</value>
</data>
<data name="ValidationExceptionMessageNoServicesInOrder" xml:space="preserve">
<value>В заказе должна быть хотя бы одна услуга</value>
</data>
<data name="ValidationExceptionMessageNotAnId" xml:space="preserve">
<value>Значение в поле {0} не является типом уникального идентификатора</value>
</data>
<data name="ValidationExceptionMessageNotInitialized" xml:space="preserve">
<value>Значение в поле {0} не проиницализировано</value>
</data>
</root>

View File

@@ -0,0 +1,20 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.StoragesContracts;
internal interface ICarStorageContract
{
List<CarDataModel> GetList(string? clientId = null, string? make = null, string? model = null);
Task<List<CarDataModel>> GetListAsync(CancellationToken ct);
CarDataModel? GetElementById(string id);
CarDataModel? GetElementByStateNumber(string stateNumber);
void AddElement(CarDataModel carDataModel);
void UpdElement(CarDataModel carDataModel);
void DelElement(string id);
}

View File

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

View File

@@ -0,0 +1,21 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.StoragesContracts;
internal interface IMaterialStorageContracts
{
List<MaterialDataModel> GetList(MaterialType? materialType = MaterialType.None);
List<MaterialHistoryDataModel> GetHistoryByMaterialId(string materialId);
MaterialDataModel? GetElementById(string id);
MaterialDataModel? GetElementByDescription(string description);
void AddElement(MaterialDataModel materialDataModel);
void UpdElement(MaterialDataModel materialDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,16 @@
using PimpMyRideContracts.DataModels;
namespace PimpMyRideContracts.StoragesContracts;
internal interface IOrderStorageContract
{
List<OrderDataModel> GetList(DateTime? startDate = null, DateTime? endDate = null, string? carId = null);
Task<List<OrderDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
OrderDataModel? GetElementById(string id);
void AddElement(OrderDataModel orderDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,17 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.StoragesContracts;
internal interface IServiceStorageContracts
{
List<ServiceDataModel> GetList(string? workerId = null, WorkType? workType = WorkType.None);
Task<List<ServiceDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
ServiceDataModel? GetElementById(string id);
void AddElement(ServiceDataModel serviceDataModel);
void DelElement(string id);
}

View File

@@ -0,0 +1,19 @@
using PimpMyRideContracts.DataModels;
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.StoragesContracts;
internal interface IWorkerStorageContract
{
List<WorkerDataModel> GetList(bool onlyActive = true, SpecialityType? specialityType = SpecialityType.None, 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);
}

View File

@@ -0,0 +1,14 @@
namespace PimpMyRideContracts.ViewModels;
public class CarViewModel
{
public required string Id { get; set; }
public required string ClientId { get; set; }
public required string Make { get; set; }
public required string Model { get; set; }
public required string StateNumber { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace PimpMyRideContracts.ViewModels;
public class ClientCarViewModel
{
public required string ClientFIO { get; set; }
public required List<string> Cars { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace PimpMyRideContracts.ViewModels;
public class ClientViewModel
{
public required string Id { get; set; }
public required string FIO { get; set; }
public required string PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,13 @@
using PimpMyRideContracts.Mapper;
namespace PimpMyRideContracts.ViewModels;
public class MaterialHistoryViewModel
{
public required string MaterialDescription { get; set; }
public double OldPrice { get; set; }
[PostProcessing(ActionType = PostProcessingType.ToLocalTime)]
public DateTime ChangeDate { get; set; }
}

View File

@@ -0,0 +1,14 @@
using PimpMyRideContracts.Enums;
namespace PimpMyRideContracts.ViewModels;
public class MaterialViewModel
{
public required string Id { get; set; }
public required string MaterialType { get; set; }
public required string Description { get; set; }
public double UnitCost { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideContracts.ViewModels;
public class OrderServiceViewModel
{
public required string OrderId { get; set; }
public required string ServiceId { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,21 @@
using PimpMyRideContracts.Mapper;
namespace PimpMyRideContracts.ViewModels;
public class OrderViewModel
{
public required string Id { get; set; }
public required string CarId { get; set; }
public required string CarMake { get; set; }
[PostProcessing(ActionType = PostProcessingType.ToLocalTime)]
public DateTime OrderDate { get; set; }
public double TotalCost { get; set; }
public bool IsCancel { get; set; }
public required List<OrderServiceViewModel> Services { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace PimpMyRideContracts.ViewModels;
public class ServiceMaterialViewModel
{
public required string ServiceId { get; set; }
public required string MaterialId { get; set; }
public int Count { get; set; }
public double Price { get; set; }
}

View File

@@ -0,0 +1,25 @@
using System.Text.Json;
using PimpMyRideContracts.Infrastructure.ServiceConfiguration;
using PimpMyRideContracts.Mapper;
namespace PimpMyRideContracts.ViewModels;
public class ServiceViewModel
{
public required string Id { get; set; }
public required string WorkerId { get; set; }
public required string WorkerFIO { get; set; }
public required string WorkType { get; set; }
[AlternativeName("ConfigurationModel")]
[PostProcessing(MappingCallMethodName = "ParseConfiguration")]
public required string Configuration { get; set; }
private string ParseConfiguration(ServiceConfiguration model) =>
JsonSerializer.Serialize(model, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
public required List<ServiceMaterialViewModel> Materials { get; set; }
}

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