26 Commits
lab-3 ... lab-8

Author SHA1 Message Date
62b4ea6378 add 2025-05-23 02:07:39 +04:00
7ebeed213a lab-8 ready 2025-05-23 02:06:42 +04:00
866b990167 commit 2025-05-23 00:54:06 +04:00
e7fdf6e526 mb push 2025-04-25 10:36:53 +04:00
b331b2f885 Big fix 2025-04-25 10:36:24 +04:00
b03e6322c0 wip trash for 2025-04-25 09:04:29 +04:00
76c62c737f wip zheny neeed fix Expected: String containing "Не найден элемент по данным"
But was:  "AdapterMessageElementNotFoundByIdException"
2025-04-25 04:28:07 +04:00
8fa02a428f lab5 ready 2025-04-25 01:55:16 +04:00
00a8846f33 some fix 2025-04-15 15:20:49 +04:00
c692f50d0d migration 2025-04-15 00:56:59 +04:00
e7dcfd6f30 fix 2025-04-15 00:49:00 +04:00
a3e1230e5c lab5 fix 2025-04-14 23:31:11 +04:00
8f5c38d239 lab5-ready 2025-04-14 23:25:14 +04:00
dbc3fb8394 Merge branch 'refs/heads/lab-4' into lab-5 2025-04-14 22:48:14 +04:00
524bfca4d0 ready 2025-04-14 22:46:08 +04:00
6b7212a13d edit 2025-04-14 22:38:00 +04:00
4113d2ab9e Merge remote-tracking branch 'origin/lab-4' into lab-4 2025-04-14 22:17:32 +04:00
d295b50e32 little update 2025-04-14 22:17:22 +04:00
f9966ae73d little update 2025-04-14 22:15:04 +04:00
d84b3850d6 lab4 2025-04-14 22:07:51 +04:00
6449c0df73 lab4 2025-04-11 10:16:19 +04:00
7beb84b967 lab4 ready 2025-04-11 01:55:51 +04:00
082111e301 lab4 ? 2025-04-11 01:36:54 +04:00
14b48e3a30 may be work 2025-04-09 11:17:10 +04:00
f649754da7 lab4-preready 2025-04-09 01:46:17 +04:00
cec7b9240e Ready full 2025-03-13 16:04:10 +04:00
282 changed files with 18890 additions and 8094 deletions

2
.gitignore vendored
View File

@@ -10,7 +10,7 @@
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
*.idea
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -1,3 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("CandyHouseTests")]

View File

@@ -1,72 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableDefaultItems>false</EnableDefaultItems>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="DataModels\IngredientDataModel.cs" />
<Compile Include="DataModels\OrderDataModel.cs" />
<Compile Include="DataModels\PekarDataModel.cs" />
<Compile Include="DataModels\PekarHistoryDataModel.cs" />
<Compile Include="DataModels\PositionDataModel.cs" />
<Compile Include="DataModels\ProductDataModel.cs" />
<Compile Include="DataModels\RecipeDataModel.cs" />
<Compile Include="DataModels\SalaryDataModel.cs" />
<Compile Include="Enums\PositionType.cs" />
<Compile Include="Enums\StatusType.cs" />
<Compile Include="Exceptions\DateTimeExtensions.cs" />
<Compile Include="Exceptions\ElementDeletedException.cs" />
<Compile Include="Exceptions\ElementExistsException.cs" />
<Compile Include="Exceptions\ElementNotFoundException.cs" />
<Compile Include="Exceptions\IncorrectDatesException.cs" />
<Compile Include="Exceptions\NullListException.cs" />
<Compile Include="Exceptions\StorageException.cs" />
<Compile Include="Exceptions\StringExtensions.cs" />
<Compile Include="Exceptions\ValidationException.cs" />
<Compile Include="Implementations\IngredientBusinessLogicContract.cs" />
<Compile Include="Implementations\OrderBusinessLogicContract.cs" />
<Compile Include="Implementations\PekarBusinessLogicContract.cs" />
<Compile Include="Implementations\PositionBusinessLogicContract.cs" />
<Compile Include="Implementations\ProductBusinessLogicContract.cs" />
<Compile Include="Implementations\SalaryBusinessLogicContract.cs" />
<Compile Include="Infrastructure\Infrastructure.cs" />
<Compile Include="Infrastructure\IValidation.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\IIngredientBusinessLogicContact.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\IOrderBusinessLogicContact.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\IPekarBusinessLogicContact.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\IPositionBusinessLogicContact.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\IProductBusinessLogicContact.cs" />
<Compile Include="Interfaces\BusinessLogicsContracts\ISalaryBusinessLogicContact.cs" />
<Compile Include="Interfaces\StoragesContracts\IIngredientStorageContact.cs" />
<Compile Include="Interfaces\StoragesContracts\IOrderStorageContact.cs" />
<Compile Include="Interfaces\StoragesContracts\IPekarStorageContact.cs" />
<Compile Include="Interfaces\StoragesContracts\IPositionStorageContact.cs" />
<Compile Include="Interfaces\StoragesContracts\IProductStorageContact.cs" />
<Compile Include="Interfaces\StoragesContracts\ISalaryStorageContact.cs" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CatHasPawsTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>CandyHouseTests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-preview.1.25080.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8" />
</ItemGroup>
</Project>

View File

@@ -1,31 +0,0 @@
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class IngredientDataModel : IValidation
{
public string Id { get; private set; }
public string Name { get; private set; }
public string Unit { get; private set; }
public decimal Cost { get; private set; }
public IngredientDataModel(string id, string name, string unit, decimal cost)
{
Id = id;
Name = name;
Unit = unit;
Cost = cost;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
if (Name.IsEmpty()) throw new ValidationException("Field Name is empty");
if (Unit.IsEmpty()) throw new ValidationException("Field Unit is empty");
if (Cost < 0) throw new ValidationException("Cost must be non-negative");
}
}
}

View File

@@ -1,45 +0,0 @@
using System;
using CandyHouseBase.Enums;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class OrderDataModel : IValidation
{
public string Id { get; private set; }
public string CustomerName { get; private set; } // Может быть null, если клиент разовый
public DateTime OrderDate { get; private set; }
public decimal TotalAmount { get; private set; }
public decimal DiscountAmount { get; private set; }
public string ProductId { get; private set; }
public string PekarId { get; private set; }
public StatusType StatusType { get; private set; }
public OrderDataModel(string id, string customerName, DateTime orderDate, decimal totalAmount,
decimal discountAmount, string productId, string pekarId, StatusType statusType)
{
Id = id;
CustomerName = customerName;
OrderDate = orderDate;
TotalAmount = totalAmount;
DiscountAmount = discountAmount;
ProductId = productId;
PekarId = pekarId;
StatusType = statusType;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
if (CustomerName.IsEmpty())
throw new ValidationException("CustomerName is empty");
if (TotalAmount < 0) throw new ValidationException("TotalAmount cannot be negative");
if (DiscountAmount < 0) throw new ValidationException("DiscountAmount cannot be negative");
if (ProductId.IsEmpty()) throw new ValidationException("Field productId is empty");
if (!ProductId.IsGuid()) throw new ValidationException("productId must be a GUID");
}
}
}

View File

@@ -1,46 +0,0 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Infrastructure;
using CandyHouseBase.Extensions;
namespace CandyHouseBase.DataModels
{
public class PekarDataModel : IValidation
{
public string Id { get; private set; }
public string FIO { get; private set; }
public string Position { get; private set; }
public decimal BonusCoefficient { get; private set; }
public bool IsDeleted { get; private set; }
public List<ProductDataModel> ProductsItems { get; private set; }
public PekarDataModel()
{
ProductsItems = new List<ProductDataModel>();
}
public PekarDataModel(string id, string fio, string position, decimal bonusCoefficient,
List<ProductDataModel> productsItems)
{
Id = id;
FIO = fio;
Position = position;
BonusCoefficient = bonusCoefficient;
ProductsItems = productsItems;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
if (FIO.IsEmpty()) throw new ValidationException("Field FIO is empty");
var fioPattern = @"^[A-Za-zА-Яа-яЁё\s\-]+$";
if (!Regex.IsMatch(FIO, fioPattern))
throw new ValidationException("FIO contains invalid characters");
if (Position.IsEmpty()) throw new ValidationException("Field Position is empty");
if (!Position.IsGuid()) throw new ValidationException("Field must be a GUID");
if (BonusCoefficient <= 0) throw new ValidationException("BonusCoefficient must be positive");
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Infrastructure;
using CandyHouseBase.Extensions;
namespace CandyHouseBase.DataModels
{
public class PekarHistoryDataModel : IValidation
{
public string PekarId { get; private set; }
public string FIO { get; private set; }
public string PositionId { get; private set; }
public DateTime Date { get; private set; }
public decimal BonusCoefficient { get; private set; }
public PekarHistoryDataModel(string peKarId, string fio, string positionId, decimal bonusCoefficient, DateTime dateTime)
{
PekarId = peKarId;
FIO = fio;
PositionId = positionId;
BonusCoefficient = bonusCoefficient;
Date = dateTime;
}
public void Validate()
{
if (PekarId.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!PekarId.IsGuid()) throw new ValidationException("Id must be a GUID");
if (FIO.IsEmpty()) throw new ValidationException("Field FIO is empty");
if (PositionId.IsEmpty()) throw new ValidationException("Field Position is empty");
if (!PositionId.IsGuid()) throw new ValidationException("Field must be a GUID");
if (BonusCoefficient <= 0) throw new ValidationException("BonusCoefficient must be positive");
}
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Text.RegularExpressions;
using CandyHouseBase.Enums;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class PositionDataModel : IValidation
{
public string Id { get; set; }
public PositionType Type { get; set; }
public string Title { get; set; }
public PositionDataModel(string id, PositionType type, string title)
{
Id = id;
Type = type;
Title = title;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
var titlePattern = @"^[A-Za-zА-Яа-яЁё\s\-]+$";
if (!Regex.IsMatch(Title, titlePattern))
throw new ValidationException("FIO contains invalid characters");
if (string.IsNullOrEmpty(Title)) throw new ValidationException("Field Title is empty");
if (!Enum.IsDefined(typeof(PositionType), Type)) throw new ValidationException("Invalid PositionType");
}
}
}

View File

@@ -1,74 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class ProductDataModel : IValidation
{
public string Id { get; private set; }
public string Name
{
get => name;
set
{
if (!name.IsEmpty()) OldName = name;
name = value.Trim();
}
}
public string Description
{
get => description;
set
{
if (!description.IsEmpty()) OldDescription = description;
description = value.Trim();
}
}
public string? OldName { get; private set; }
public string? OldDescription { get; private set; }
private string name;
private string description;
public List<IngredientDataModel> IngredientsItems { get; private set; }
public ProductDataModel()
{
IngredientsItems = new List<IngredientDataModel>();
}
public ProductDataModel(string id, string name, string description, List<IngredientDataModel> ingredients)
{
Id = id;
Name = name;
Description = description;
IngredientsItems = ingredients;
}
public ProductDataModel(string id, string name, string description, string oldName, string oldDescription,
List<IngredientDataModel> ingredients)
{
Id = id;
Name = name;
Description = description;
IngredientsItems = ingredients;
OldName = oldName;
OldDescription = oldDescription;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
if (Name.IsEmpty()) throw new ValidationException("Field Name is empty");
if (Description.IsEmpty()) throw new ValidationException("Field Description is empty");
if (IngredientsItems.Count == 0) throw new ValidationException("Field IngredientsItems is empty");
}
}
}

View File

@@ -1,27 +0,0 @@
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class RecipeDataModel : IValidation
{
public string ProductId { get; private set; }
public string IngredientId { get; private set; }
public int Quantity { get; private set; }
public RecipeDataModel(string productId, string ingredientId, int quantity)
{
ProductId = productId;
IngredientId = ingredientId;
Quantity = quantity;
}
public void Validate()
{
if (!ProductId.IsGuid()) throw new ValidationException("ProductId must be a GUID");
if (!IngredientId.IsGuid()) throw new ValidationException("IngredientId must be a GUID");
if (Quantity <= 0) throw new ValidationException("Quantity must be positive");
}
}
}

View File

@@ -1,38 +0,0 @@
using System;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Infrastructure;
namespace CandyHouseBase.DataModels
{
public class SalaryDataModel : IValidation
{
public string Id { get; private set; }
public string PekarId { get; private set; }
public DateTime Period { get; private set; }
public decimal BaseRate { get; private set; }
public decimal BonusRate { get; private set; }
public decimal TotalSalary { get; private set; }
public SalaryDataModel(string id, string pekarId, DateTime period, decimal baseRate, decimal bonusRate, decimal totalSalary)
{
Id = id;
PekarId = pekarId;
Period = period;
BaseRate = baseRate;
BonusRate = bonusRate;
TotalSalary = totalSalary;
}
public void Validate()
{
if (Id.IsEmpty()) throw new ValidationException("Field Id is empty");
if (!Id.IsGuid()) throw new ValidationException("Id must be a GUID");
if (PekarId.IsEmpty()) throw new ValidationException("Field PekarId is empty");
if (!PekarId.IsGuid()) throw new ValidationException("PekarId must be a GUID");
if (BaseRate < 0) throw new ValidationException("BaseRate cannot be negative");
if (BonusRate < 0) throw new ValidationException("BonusRate cannot be negative");
if (TotalSalary < 0) throw new ValidationException("TotalSalary cannot be negative");
}
}
}

View File

@@ -1,10 +0,0 @@
namespace CandyHouseBase.Enums
{
public enum PositionType
{
None = 1,
Small = 2,
Medium = 3,
Cool = 4,
}
}

View File

@@ -1,10 +0,0 @@
namespace CandyHouseBase.Enums
{
public enum StatusType
{
Pending,
Completed,
Cancelled,
InProgress
}
}

View File

@@ -1,3 +0,0 @@
namespace CandyHouseBase.Exceptions;
public class ElementDeletedException(string id) : Exception($"Cannot modify a deleted item (id: {id})");

View File

@@ -1,16 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions;
public class ElementExistsException : Exception
{
public string ParamName { get; private set; }
public string ParamValue { get; private set; }
public ElementExistsException(string paramName, string paramValue) :
base($"There is already an element with value {paramValue} of parameter {paramName}")
{
ParamName = paramName;
ParamValue = paramValue;
}
}

View File

@@ -1,13 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions;
public class ElementNotFoundException : Exception
{
public string Value { get; private set; }
public ElementNotFoundException(string value) : base($"Element not found at value = {value}")
{
Value = value;
}
}

View File

@@ -1,11 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions;
public class IncorrectDatesException : Exception
{
public IncorrectDatesException(DateTime start, DateTime end) : base(
$"The end date must be later than the start date.. StartDate: {start:dd.MM.YYYY}. EndDate: {end:dd.MM.YYYY}")
{
}
}

View File

@@ -1,10 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions;
public class NullListException : Exception
{
public NullListException() : base("The returned list is null")
{
}
}

View File

@@ -1,5 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions;
public class StorageException(Exception ex) : Exception($"Error while working in storage: {ex.Message}", ex);

View File

@@ -1,10 +0,0 @@
using System;
namespace CandyHouseBase.Extensions
{
public static class StringExtensions
{
public static bool IsEmpty(this string str) => string.IsNullOrWhiteSpace(str);
public static bool IsGuid(this string str) => Guid.TryParse(str, out _);
}
}

View File

@@ -1,9 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions
{
public class ValidationException : Exception
{
public ValidationException(string message) : base(message) { }
}
}

View File

@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using CandyHouseBase.DataModels;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class IngredientBusinessLogicContract(
IIngredientStorageContact ingredientStorageContact,
ILogger logger)
: IIngredientBusinessLogicContact
{
private readonly IIngredientStorageContact _ingredientStorageContact = ingredientStorageContact;
private readonly ILogger _logger = logger;
public List<IngredientDataModel> GetAllIngredients()
{
_logger.LogInformation("GetAllIngredients");
var ingredients = _ingredientStorageContact.GetList() ?? throw new NullListException();
return ingredients;
}
public IngredientDataModel GetIngredientByData(string data)
{
_logger.LogInformation("GetIngredientByData for data: {data}", data);
if (data == null)
throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(data))
throw new ArgumentNullException(nameof(data));
if (data.IsGuid())
{
var ingredient = _ingredientStorageContact.GetElementById(data);
if (ingredient == null)
throw new ElementNotFoundException(data);
return ingredient;
}
else
{
var ingredient = _ingredientStorageContact.GetElementByName(data);
if (ingredient == null)
throw new ElementNotFoundException(data);
return ingredient;
}
}
public void InsertIngredient(IngredientDataModel ingredient)
{
_logger.LogInformation("InsertIngredient: {json}", JsonSerializer.Serialize(ingredient));
if (ingredient == null)
throw new ArgumentNullException(nameof(ingredient));
ingredient.Validate();
_ingredientStorageContact.AddElement(ingredient);
}
public void UpdateIngredient(IngredientDataModel ingredient)
{
_logger.LogInformation("UpdateIngredient: {json}", JsonSerializer.Serialize(ingredient));
if (ingredient == null)
throw new ArgumentNullException(nameof(ingredient));
ingredient.Validate();
_ingredientStorageContact.UpdateElement(ingredient);
}
public void DeleteIngredient(string id)
{
_logger.LogInformation("DeleteIngredient for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var ingredient = _ingredientStorageContact.GetElementById(id);
if (ingredient == null)
throw new ElementNotFoundException(id);
_ingredientStorageContact.DeleteElement(id);
}
}
}

View File

@@ -1,126 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using CandyHouseBase.DataModels;
using CandyHouseBase.Enums;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class OrderBusinessLogicContract(
IOrderStorageContact orderStorageContact,
IPekarStorageContact pekarStorageContact,
IProductStorageContact productStorageContact,
ILogger logger)
: IOrderBusinessLogicContact
{
private readonly IOrderStorageContact _orderStorageContact = orderStorageContact;
private readonly IPekarStorageContact _pekarStorageContact = pekarStorageContact;
private readonly IProductStorageContact _productStorageContact = productStorageContact;
private readonly ILogger _logger = logger;
public List<OrderDataModel> GetAllOrders()
{
_logger.LogInformation("GetAllOrders");
var orders = _orderStorageContact.GetOrders() ?? throw new NullListException();
return orders;
}
public OrderDataModel GetOrderByData(string data)
{
_logger.LogInformation("GetOrderByData for data: {data}", data);
if (data == null)
throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(data))
throw new ArgumentNullException(nameof(data));
if (!data.IsGuid())
throw new ValidationException("data must be a GUID");
var order = _orderStorageContact.GetElementById(data) ?? throw new ElementNotFoundException(data);
return order;
}
public void InsertOrder(OrderDataModel order)
{
_logger.LogInformation("InsertOrder: {json}", JsonSerializer.Serialize(order));
if (order == null)
throw new ArgumentNullException(nameof(order));
order.Validate();
// Check if Pekar exists
if (_pekarStorageContact.GetElementById(order.PekarId) == null)
throw new ElementNotFoundException(order.PekarId);
// Check if Product exists
if (_productStorageContact.GetElementById(order.ProductId) == null)
throw new ElementNotFoundException(order.ProductId);
// Check if order with this ID already exists
var existingOrder = _orderStorageContact.GetElementById(order.Id);
if (existingOrder != null)
throw new ElementExistsException("ID", order.Id);
try
{
_orderStorageContact.AddElement(order);
}
catch (Exception ex)
{
throw new StorageException(ex);
}
}
public void UpdateOrder(OrderDataModel order)
{
_logger.LogInformation("UpdateOrder: {json}", JsonSerializer.Serialize(order));
if (order == null)
throw new ArgumentNullException(nameof(order));
order.Validate();
// Check if Pekar exists
if (_pekarStorageContact.GetElementById(order.PekarId) == null)
throw new ElementNotFoundException(order.PekarId);
// Check if Product exists
if (_productStorageContact.GetElementById(order.ProductId) == null)
throw new ElementNotFoundException(order.ProductId);
try
{
_orderStorageContact.UpdateElement(order);
}
catch (Exception ex)
{
throw new StorageException(ex);
}
}
public void DeleteOrder(string id)
{
_logger.LogInformation("DeleteOrder for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var order = _orderStorageContact.GetElementById(id);
if (order == null)
throw new ElementNotFoundException(id);
try
{
_orderStorageContact.DeleteElement(order);
}
catch (Exception ex)
{
throw new StorageException(ex);
}
}
}
}

View File

@@ -1,174 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.RegularExpressions;
using CandyHouseBase.DataModels;
using CandyHouseBase.Enums;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class PekarBusinessLogicContract(
IPekarStorageContact pekarStorageContact,
IProductStorageContact productStorageContact,
IPositionStorageContact positionStorageContact,
ILogger logger)
: IPekarBusinessLogicContact
{
private readonly IPekarStorageContact _pekarStorageContact = pekarStorageContact;
private readonly IProductStorageContact _productStorageContact = productStorageContact;
private readonly IPositionStorageContact _positionStorageContact = positionStorageContact;
private readonly ILogger _logger = logger;
public List<PekarDataModel> GetAllPekars()
{
_logger.LogInformation("GetAllPekars");
var pekars = _pekarStorageContact.GetList() ?? throw new NullListException();
return pekars;
}
public List<PekarDataModel> GetAllDataOfPekar(string pekarId)
{
_logger.LogInformation("GetAllDataOfPekar for pekarId: {pekarId}", pekarId);
if (pekarId.IsEmpty())
throw new ArgumentNullException(nameof(pekarId));
if (!pekarId.IsGuid())
throw new ValidationException("pekarId must be a GUID");
var pekarsWithHistory = _pekarStorageContact.GetPekarWithHistory(pekarId);
if (pekarsWithHistory == null || !pekarsWithHistory.Any())
throw new ElementNotFoundException(pekarId);
var historyRecords = new List<PekarDataModel>();
foreach (var pekar in pekarsWithHistory)
{
Guid positionGuid;
if (!Guid.TryParse(pekar.Position, out positionGuid))
{
_logger.LogWarning("Invalid Position GUID: {Position}", pekar.Position);
continue;
}
var position = _positionStorageContact.GetElementById(positionGuid.ToString());
if (position == null)
throw new ElementNotFoundException(pekar.Position);
historyRecords.Add(new PekarDataModel(pekar.Id, pekar.FIO, position.Id, pekar.BonusCoefficient,
new List<ProductDataModel>()));
}
if (!historyRecords.Any())
{
var firstPekar = pekarsWithHistory.First();
Guid positionGuid;
if (Guid.TryParse(firstPekar.Position, out positionGuid))
{
var position = _positionStorageContact.GetElementById(positionGuid.ToString());
if (position != null)
historyRecords.Add(new PekarDataModel(firstPekar.Id, firstPekar.FIO, position.Id,
firstPekar.BonusCoefficient,
new List<ProductDataModel>()));
}
}
return historyRecords;
}
public PekarDataModel GetPekarByData(string data)
{
_logger.LogInformation("GetPekarByData for data: {data}", data);
if (data.IsEmpty())
throw new ArgumentNullException(nameof(data));
if (!data.IsGuid() && !Regex.IsMatch(data, @"^[A-Za-zА-Яа-яЁё\s\-]+$"))
throw new ValidationException("data must be a GUID or FIO");
var pekar = _pekarStorageContact.GetElementById(data) ?? _pekarStorageContact.GetElementByFio(data) ??
throw new ElementNotFoundException(data);
return pekar;
}
public void InsertPekar(PekarDataModel pekar)
{
_logger.LogInformation("InsertPekar: {json}", JsonSerializer.Serialize(pekar));
if (pekar == null)
throw new ArgumentNullException(nameof(pekar));
pekar.Validate();
var existingPekar = _pekarStorageContact.GetElementById(pekar.Id);
if (existingPekar != null)
{
var history = new PekarHistoryDataModel(
existingPekar.Id,
existingPekar.FIO,
existingPekar.Position,
existingPekar.BonusCoefficient,
DateTime.Now
);
}
foreach (var product in pekar.ProductsItems)
{
if (_productStorageContact.GetElementById(product.Id) == null)
throw new ElementNotFoundException(product.Id);
}
_pekarStorageContact.AddElement(pekar);
}
public void UpdatePekar(PekarDataModel pekar)
{
_logger.LogInformation("UpdatePekar: {json}", JsonSerializer.Serialize(pekar));
if (pekar == null)
throw new ArgumentNullException(nameof(pekar));
pekar.Validate();
foreach (var product in pekar.ProductsItems)
{
if (_productStorageContact.GetElementById(product.Id) == null)
throw new ElementNotFoundException(product.Id);
}
_pekarStorageContact.UpdateElement(pekar);
}
public void DeletePekar(string id)
{
_logger.LogInformation("DeletePekar for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var pekar = _pekarStorageContact.GetElementById(id);
if (pekar == null)
throw new ElementNotFoundException(id);
_pekarStorageContact.DeleteElement(id);
}
public void RestorePekar(string id)
{
_logger.LogInformation("RestorePekar for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var pekar = _pekarStorageContact.GetElementById(id);
if (pekar == null)
throw new ElementNotFoundException(id);
_pekarStorageContact.RestoreElement(id);
}
}
}

View File

@@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using CandyHouseBase.DataModels;
using CandyHouseBase.Enums;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class PositionBusinessLogicContract(
IPositionStorageContact positionStorageContact,
ILogger logger)
: IPositionBusinessLogicContact
{
private readonly IPositionStorageContact _positionStorageContact = positionStorageContact;
private readonly ILogger _logger = logger;
public List<PositionDataModel> GetAllPositions()
{
_logger.LogInformation("GetAllPositions");
var positions = _positionStorageContact.GetList() ?? throw new NullListException();
return positions;
}
public PositionDataModel GetPositionByData(string data)
{
_logger.LogInformation("GetPositionByData for data: {data}", data);
if (data.IsEmpty())
throw new ArgumentNullException(nameof(data));
var position = _positionStorageContact.GetElementById(data);
if (position == null)
throw new ElementNotFoundException(data);
if (!data.IsGuid())
throw new ValidationException("data must be a GUID");
return position;
}
public void InsertPosition(PositionDataModel position)
{
_logger.LogInformation("InsertPosition: {json}", JsonSerializer.Serialize(position));
if (position == null)
throw new ArgumentNullException(nameof(position));
position.Validate();
_positionStorageContact.AddElement(position);
}
public void UpdatePosition(PositionDataModel position)
{
_logger.LogInformation("UpdatePosition: {json}", JsonSerializer.Serialize(position));
if (position == null)
throw new ArgumentNullException(nameof(position));
position.Validate();
_positionStorageContact.UpdateElement(position);
}
public void DeletePosition(string id)
{
_logger.LogInformation("DeletePosition for id: {id}", id);
if (id.IsEmpty())
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var position = _positionStorageContact.GetElementById(id);
if (position == null)
throw new ElementNotFoundException(id);
try
{
_positionStorageContact.DeleteElement(id);
}
catch (Exception ex)
{
throw new StorageException(ex);
}
}
}
}

View File

@@ -1,114 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using CandyHouseBase.DataModels;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class ProductBusinessLogicContract(
IProductStorageContact productStorageContact,
IIngredientStorageContact ingredientStorageContact,
ILogger logger)
: IProductBusinessLogicContact
{
private readonly IProductStorageContact _productStorageContact = productStorageContact;
private readonly IIngredientStorageContact _ingredientStorageContact = ingredientStorageContact;
private readonly ILogger _logger = logger;
public List<ProductDataModel> GetAllProducts()
{
_logger.LogInformation("GetAllProducts");
var products = _productStorageContact.GetList() ?? throw new NullListException();
return products;
}
public ProductDataModel GetProductByData(string data)
{
_logger.LogInformation("GetProductByData for data: {data}", data);
if (data == null)
throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(data))
throw new ArgumentNullException(nameof(data));
if (data.IsGuid())
{
var product = _productStorageContact.GetElementById(data);
if (product == null)
throw new ElementNotFoundException(data);
if (!data.IsGuid()) // Validate after existence check (redundant but kept for consistency)
throw new ValidationException("data must be a GUID");
return product;
}
else
{
var products = _productStorageContact.GetList();
var productByName = products.FirstOrDefault(p => p.Name == data || (p.OldName != null && p.OldName == data));
if (productByName == null)
throw new ElementNotFoundException(data);
return productByName;
}
}
public void InsertProduct(ProductDataModel product)
{
_logger.LogInformation("InsertProduct: {json}", JsonSerializer.Serialize(product));
if (product == null)
throw new ArgumentNullException(nameof(product));
product.Validate();
foreach (var ingredient in product.IngredientsItems)
{
if (_ingredientStorageContact.GetElementById(ingredient.Id) == null)
throw new ElementNotFoundException(ingredient.Id);
}
_productStorageContact.AddElement(product);
}
public void UpdateProduct(ProductDataModel product)
{
_logger.LogInformation("UpdateProduct: {json}", JsonSerializer.Serialize(product));
if (product == null)
throw new ArgumentNullException(nameof(product));
product.Validate();
foreach (var ingredient in product.IngredientsItems)
{
if (_ingredientStorageContact.GetElementById(ingredient.Id) == null)
throw new ElementNotFoundException(ingredient.Id);
}
_productStorageContact.UpdateElement(product);
}
public void DeleteProduct(string id)
{
_logger.LogInformation("DeleteProduct for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var product = _productStorageContact.GetElementById(id);
if (product == null)
throw new ElementNotFoundException(id);
try
{
_productStorageContact.DeleteElement(id);
}
catch (Exception ex)
{
throw new StorageException(ex);
}
}
}
}

View File

@@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using CandyHouseBase.DataModels;
using CandyHouseBase.Exceptions;
using CandyHouseBase.Extensions;
using CandyHouseBase.Interfaces.BusinessLogicsContracts;
using CandyHouseBase.Interfaces.StoragesContracts;
using Microsoft.Extensions.Logging;
namespace CandyHouseBase.Implementations
{
internal class SalaryBusinessLogicContract(
ISalaryStorageContact salaryStorageContact,
IPekarStorageContact pekarStorageContact,
ILogger logger)
: ISalaryBusinessLogicContact
{
private readonly ISalaryStorageContact _salaryStorageContact = salaryStorageContact;
private readonly IPekarStorageContact _pekarStorageContact = pekarStorageContact;
private readonly ILogger _logger = logger;
public List<SalaryDataModel> GetAllSalaries()
{
_logger.LogInformation("GetAllSalaries");
var salaries = _salaryStorageContact.GetList() ?? throw new NullListException();
return salaries;
}
public SalaryDataModel GetSalaryByData(string data)
{
_logger.LogInformation("GetSalaryByData for data: {data}", data);
if (data == null)
throw new ArgumentNullException(nameof(data));
if (string.IsNullOrEmpty(data))
throw new ArgumentNullException(nameof(data));
if (!data.IsGuid())
throw new ValidationException("data must be a GUID");
var salary = _salaryStorageContact.GetElementById(data) ?? throw new ElementNotFoundException(data);
if (_pekarStorageContact.GetElementById(salary.PekarId) == null)
throw new ElementNotFoundException(salary.PekarId);
return salary;
}
public void InsertSalary(SalaryDataModel salary)
{
_logger.LogInformation("InsertSalary: {json}", JsonSerializer.Serialize(salary));
if (salary == null)
throw new ArgumentNullException(nameof(salary));
salary.Validate();
if (_pekarStorageContact.GetElementById(salary.PekarId) == null)
throw new ElementNotFoundException(salary.PekarId);
_salaryStorageContact.AddElement(salary);
}
public void UpdateSalary(SalaryDataModel salary)
{
_logger.LogInformation("UpdateSalary: {json}", JsonSerializer.Serialize(salary));
if (salary == null)
throw new ArgumentNullException(nameof(salary));
salary.Validate();
if (_pekarStorageContact.GetElementById(salary.PekarId) == null)
throw new ElementNotFoundException(salary.PekarId);
_salaryStorageContact.UpdateElement(salary);
}
public void DeleteSalary(string id)
{
_logger.LogInformation("DeleteSalary for id: {id}", id);
if (id == null)
throw new ArgumentNullException(nameof(id));
if (string.IsNullOrEmpty(id))
throw new ArgumentNullException(nameof(id));
if (!id.IsGuid())
throw new ValidationException("id must be a GUID");
var salary = _salaryStorageContact.GetElementById(id);
if (salary == null)
throw new ElementNotFoundException(id);
_salaryStorageContact.DeleteElement(id);
}
}
}

View File

@@ -1,7 +0,0 @@
namespace CandyHouseBase.Infrastructure
{
public interface IValidation
{
void Validate();
}
}

View File

@@ -1,7 +0,0 @@
namespace CandyHouseBase.Infrastructure;
public interface IConfigurationDatabase
{
public string ConnectionString =>
"Host=127.0.0.1;Port=5432;Database=CandyHouseTest;Username=postgres;Password=postgres;";
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface IIngredientBusinessLogicContact
{
List<IngredientDataModel> GetAllIngredients();
IngredientDataModel GetIngredientByData(string data);
void InsertIngredient(IngredientDataModel ingredient);
void UpdateIngredient(IngredientDataModel ingredient);
void DeleteIngredient(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface IOrderBusinessLogicContact
{
List<OrderDataModel> GetAllOrders();
OrderDataModel GetOrderByData(string data);
void InsertOrder(OrderDataModel order);
void UpdateOrder(OrderDataModel order);
void DeleteOrder(string id);
}
}

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface IPekarBusinessLogicContact
{
List<PekarDataModel> GetAllPekars();
List<PekarDataModel> GetAllDataOfPekar(string pekarId);
PekarDataModel GetPekarByData(string data);
void InsertPekar(PekarDataModel order);
void UpdatePekar(PekarDataModel order);
void DeletePekar(string id);
void RestorePekar(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface IPositionBusinessLogicContact
{
List<PositionDataModel> GetAllPositions();
PositionDataModel GetPositionByData(string data);
void InsertPosition(PositionDataModel position);
void UpdatePosition(PositionDataModel position);
void DeletePosition(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface IProductBusinessLogicContact
{
List<ProductDataModel> GetAllProducts();
ProductDataModel GetProductByData(string data);
void InsertProduct(ProductDataModel product);
void UpdateProduct(ProductDataModel product);
void DeleteProduct(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.BusinessLogicsContracts
{
public interface ISalaryBusinessLogicContact
{
List<SalaryDataModel> GetAllSalaries();
SalaryDataModel GetSalaryByData(string data);
void InsertSalary(SalaryDataModel salary);
void UpdateSalary(SalaryDataModel salary);
void DeleteSalary(string id);
}
}

View File

@@ -1,15 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface IIngredientStorageContact
{
List<IngredientDataModel> GetList();
IngredientDataModel GetElementById(string id);
IngredientDataModel GetElementByName(string name);
void AddElement(IngredientDataModel ingredient);
void UpdateElement(IngredientDataModel ingredient);
void DeleteElement(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface IOrderStorageContact
{
List<OrderDataModel> GetOrders();
void AddElement(OrderDataModel order);
void UpdateElement(OrderDataModel order);
void DeleteElement(OrderDataModel order);
OrderDataModel GetElementById(string orderId);
}
}

View File

@@ -1,17 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface IPekarStorageContact
{
List<PekarDataModel> GetList();
List<PekarDataModel> GetPekarWithHistory(string id);
PekarDataModel GetElementById(string id);
PekarDataModel GetElementByFio(string fio);
void AddElement(PekarDataModel item);
void UpdateElement(PekarDataModel item);
void DeleteElement(string id);
void RestoreElement(string id);
}
}

View File

@@ -1,16 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface IProductStorageContact
{
List<ProductDataModel> GetList();
ProductDataModel GetElementById(string id);
ProductDataModel GetElementByName(string name);
ProductDataModel GetElementByOldName(string name);
void AddElement(ProductDataModel element);
void UpdateElement(ProductDataModel element);
void DeleteElement(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface IPositionStorageContact
{
List<PositionDataModel> GetList();
PositionDataModel GetElementById(string id);
void AddElement(PositionDataModel element);
void UpdateElement(PositionDataModel element);
void DeleteElement(string id);
}
}

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using CandyHouseBase.DataModels;
namespace CandyHouseBase.Interfaces.StoragesContracts
{
public interface ISalaryStorageContact
{
List<SalaryDataModel> GetList();
SalaryDataModel GetElementById(string id);
void AddElement(SalaryDataModel element);
void UpdateElement(SalaryDataModel element);
void DeleteElement(string id);
}
}

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="CandyHouseTests" />
<InternalsVisibleTo Include="CandyHouseWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CandyHouseContracts\CandyHouseContracts.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,77 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace CandyHouseBusinessLogic.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, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
{
return _clientStorageContract.GetElementByPhoneNumber(data) ?? throw new ElementNotFoundException(data, _localizer);
}
if (Regex.IsMatch(data, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"))
{
return _clientStorageContract.GetElementByEmail(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(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_clientStorageContract.DelElement(id);
}
}

View File

@@ -0,0 +1,141 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Infrastructure.ClientConfigurations;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
namespace CandyHouseBusinessLogic.Implementations;
internal class ClientDiscountBusinessLogicContract(IClientDiscountStorageContract clientDiscountStorageContract,
ISaleStorageContract saleStorageContract, IClientStorageContract clientStorageContract, IStringLocalizer<Messages> localizer, ILogger logger, IConfigurationClientDiscount сonfiguration) : IClientDiscountBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IClientDiscountStorageContract _clientDiscountStorageContract = clientDiscountStorageContract;
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
private readonly IClientStorageContract _clientStorageContract = clientStorageContract;
private readonly IConfigurationClientDiscount _clientDiscountConfiguration = сonfiguration;
private readonly IStringLocalizer<Messages> _localizer = localizer;
private readonly Lock _lockObject = new();
public List<ClientDiscountDataModel> GetAllClientDiscountsByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllClientDiscounts params: {fromDate}, {toDate}", fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _clientDiscountStorageContract.GetList(fromDate, toDate);
}
public List<ClientDiscountDataModel> GetAllClientDiscountsByPeriodByClient(DateTime fromDate, DateTime toDate, string clientId)
{
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (clientId.IsEmpty())
{
throw new ArgumentNullException(nameof(clientId));
}
if (!clientId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ClientId"));
}
_logger.LogInformation("GetAllClientDiscounts params: {fromDate}, {toDate}, {clientId}", fromDate, toDate, clientId);
return _clientDiscountStorageContract.GetList(fromDate, toDate, clientId);
}
public void CalculateClientDiscountByMonth(DateTime date)
{
_logger.LogInformation("CalculateClientDiscountByMounth: {date}", date);
var startDate = new DateTime(date.Year, date.Month, 1).ToUniversalTime();
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month)).ToUniversalTime();
var clients = _clientStorageContract.GetList();
foreach (var client in clients)
{
var sales = _saleStorageContract.GetList(startDate, finishDate, clientId: client.Id);
double discountAmount = client.ConfigurationModel switch
{
null => 0,
SilverClientConfiguration sbc => CalculateClientDiscountForSilverClient(sales, startDate, finishDate, sbc),
GoldenClientConfiguration gbc => CalculateClientDiscountForGoldenClient(sales, startDate, finishDate, gbc),
ClientConfiguration bc => bc.BasicLevel,
};
_logger.LogDebug("The client {clientId} received a discount in the amount {discountAmount}", client.Id, discountAmount);
_clientDiscountStorageContract.AddElement(new ClientDiscountDataModel(client.Id, finishDate, discountAmount));
}
}
private double CalculateClientDiscountForSilverClient(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, SilverClientConfiguration config)
{
var tasks = new List<Task>();
var totalDiscount = 0.0;
var semaphore = new SemaphoreSlim(_clientDiscountConfiguration.MaxParallelThreads);
for (var date = startDate; date < finishDate; date = date.AddDays(1))
{
semaphore.Wait();
tasks.Add(Task.Factory.StartNew((object? obj) =>
{
try
{
var dateInTask = (DateTime)obj!;
var salesInDay = sales.Where(x => x.SaleDate >= dateInTask && x.SaleDate <= dateInTask.AddDays(1)).ToArray();
if (salesInDay.Length > 0)
{
lock (_lockObject)
{
totalDiscount += salesInDay.Sum(x => x.Sum) * config.SilverLevel;
}
}
}
finally
{
semaphore.Release();
}
}, date));
}
var calcBonusTask = Task.Run(() =>
{
return sales.Where(x => x.Sum > _clientDiscountConfiguration.ExtraSaleSum).Sum(x => x.Sum) * config.BonusForPurchases;
});
try
{
Task.WaitAll([Task.WhenAll(tasks), calcBonusTask]);
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
{
_logger.LogError(ex, "Error in the SilverClient discount calculation process");
}
return 0;
}
finally
{
semaphore.Dispose();
}
return config.BasicLevel + totalDiscount + calcBonusTask.Result;
}
private double CalculateClientDiscountForGoldenClient(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, GoldenClientConfiguration config)
{
try
{
return config.BasicLevel + config.GoldenLevel * sales.Count;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in GoldenClient discount calculation process");
return 0;
}
}
}

View File

@@ -0,0 +1,102 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
using System.Text.Json;
namespace CandyHouseBusinessLogic.Implementations;
internal class EmployeeBusinessLogicContract(IEmployeeStorageContract employeeStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : IEmployeeBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly IEmployeeStorageContract _employeeStorageContract = employeeStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<EmployeeDataModel> GetAllEmployees(bool onlyActive = true)
{
_logger.LogInformation("GetAllEmployees params: {onlyActive}", onlyActive);
return _employeeStorageContract.GetList(onlyActive);
}
public List<EmployeeDataModel> GetAllEmployeesByPost(string postId, bool onlyActive = true)
{
_logger.LogInformation("GetAllEmployees params: {postId}, {onlyActive},", postId, onlyActive);
if (postId.IsEmpty())
{
throw new ArgumentNullException(nameof(postId));
}
if (!postId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "PostId"));
}
return _employeeStorageContract.GetList(onlyActive, postId);
}
public List<EmployeeDataModel> GetAllEmployeesByBirthDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllEmployees params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _employeeStorageContract.GetList(onlyActive, fromBirthDate: fromDate, toBirthDate: toDate);
}
public List<EmployeeDataModel> GetAllEmployeesByEmploymentDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllEmployees params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _employeeStorageContract.GetList(onlyActive, fromEmploymentDate: fromDate, toEmploymentDate: toDate);
}
public EmployeeDataModel GetEmployeeByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _employeeStorageContract.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _employeeStorageContract.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertEmployee(EmployeeDataModel employeeDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(employeeDataModel));
ArgumentNullException.ThrowIfNull(employeeDataModel);
employeeDataModel.Validate(_localizer);
_employeeStorageContract.AddElement(employeeDataModel);
}
public void UpdateEmployee(EmployeeDataModel employeeDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(employeeDataModel));
ArgumentNullException.ThrowIfNull(employeeDataModel);
employeeDataModel.Validate(_localizer);
_employeeStorageContract.UpdElement(employeeDataModel);
}
public void DeleteEmployee(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_employeeStorageContract.DelElement(id);
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,121 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using CandyHouseBusinessLogic.OfficePackage;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
namespace CandyHouseBusinessLogic.Implementations;
internal class ReportContract : IReportContract
{
private readonly IProductStorageContract _productStorageContract;
private readonly ISaleStorageContract _saleStorageContract;
private readonly IClientDiscountStorageContract _clientDiscountStorageContract;
private readonly BaseWordBuilder _baseWordBuilder;
private readonly BaseExcelBuilder _baseExcelBuilder;
private readonly BasePdfBuilder _basePdfBuilder;
private readonly IStringLocalizer<Messages> _localizer;
private readonly ILogger _logger;
internal readonly string[] _documentHeader;
internal readonly string[] _tableHeader;
public ReportContract(IProductStorageContract productStorageContract, ISaleStorageContract saleStorageContract, IClientDiscountStorageContract clientDiscountStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, IStringLocalizer<Messages> localizer, ILogger logger)
{
_productStorageContract = productStorageContract;
_saleStorageContract = saleStorageContract;
_clientDiscountStorageContract = clientDiscountStorageContract;
_baseWordBuilder = baseWordBuilder;
_baseExcelBuilder = baseExcelBuilder;
_basePdfBuilder = basePdfBuilder;
_localizer = localizer;
_logger = logger;
_documentHeader = [_localizer["DocumentDocCaptionProduct"], _localizer["DocumentDocCaptionPrice"], _localizer["DocumentDocCaptionDate"]];
_tableHeader = [_localizer["DocumentExcelCaptionDate"], _localizer["DocumentExcelCaptionSum"],
_localizer["DocumentExcelCaptionDiscount"], _localizer["DocumentExcelCaptionEmployee"], _localizer["DocumentExcelCaptionProduct"], _localizer["DocumentExcelCaptionCount"]];
}
public async Task<Stream> CreateDocumentProductHistoryPricesAsync(CancellationToken ct)
{
_logger.LogInformation("Create report ProductHistoryPrices");
var data = await GetDataByHistoryPricesAsync(ct);
return _baseWordBuilder
.AddHeader(_localizer["DocumentDocHeader"])
.AddParagraph(string.Format(_localizer["DocumentDocSubHeader"], DateTime.Now))
.AddTable([3000, 5000, 4000], [.. new List<string[]>() { _documentHeader }.Union([.. data.SelectMany(x => (new List<string[]>() { new string[] { x.ProductName, "", "" } }).Union(x.Prices.Select(y => new string[] { "", y.Split(" (")[0], y.Split(" (")[1].TrimEnd(')') })))])])
.Build();
}
public async Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report SalesByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataBySalesAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
return _baseExcelBuilder
.AddHeader(_localizer["DocumentExcelHeader"], 0, 6)
.AddParagraph(string.Format(_localizer["DocumentExcelSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()), 2)
.AddTable([10, 10, 10, 10, 10, 10], [.. new List<string[]>() { _tableHeader }.Union(data.SelectMany(x => (new List<string[]>() { new string[] { x.SaleDate.ToShortDateString(), x.Sum.ToString("N2"), x.Discount.ToString("N2"), x.EmployeeFIO, "", "" } }).Union(x.Products!.Select(y => new string[] { "", "", "", "", y.ProductName, y.Count.ToString("N2") })).ToArray())).Union([["Всего", data.Sum(x => x.Sum).ToString("N2"), data.Sum(x => x.Discount).ToString("N2"), "", "", ""]])])
.Build();
}
public async Task<Stream> CreateDocumentClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report ClientDiscountByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataByClientDiscountAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
return _basePdfBuilder
.AddHeader(_localizer["DocumentPdfHeader"])
.AddParagraph(string.Format(_localizer["DocumentPdfSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()))
.AddPieChart("Начисления", [.. data.Select(x => (x.ClientFIO, x.TotalDiscount))])
.Build();
}
public Task<List<ProductHistoryPricesDataModel>> GetDataProductHistoryPricesAsync(CancellationToken ct)
{
_logger.LogInformation("Get data ProductHistoryPrices");
return GetDataByHistoryPricesAsync(ct);
}
public Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data SalesByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataBySalesAsync(dateStart, dateFinish, ct);
}
public Task<List<ClientDiscountByPeriodDataModel>> GetDataClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data ClientDiscountByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataByClientDiscountAsync(dateStart, dateFinish, ct);
}
private async Task<List<ProductHistoryPricesDataModel>> GetDataByHistoryPricesAsync(CancellationToken ct) => [.. (await _productStorageContract.GetListAsync(ct)).
Select(async product => new { ProductName = product.ProductName, History = await Task.Run(() => _productStorageContract.GetHistoryByProductId(product.Id))}).Select(t => t.Result).Select(x => new ProductHistoryPricesDataModel{ ProductName = x.ProductName, Prices = [.. x.History.OrderByDescending(h => h.ChangeDate).Select(h => $"{h.OldPrice:N2} ({h.ChangeDate:dd.MM.yyyy})")] })];
private async Task<List<SaleDataModel>> GetDataBySalesAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _saleStorageContract.GetListAsync(dateStart, dateFinish, ct)).OrderBy(x => x.SaleDate)];
}
private async Task<List<ClientDiscountByPeriodDataModel>> GetDataByClientDiscountAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _clientDiscountStorageContract.GetListAsync(dateStart, dateFinish, ct)).GroupBy(x => x.ClientFIO).Select(x => new ClientDiscountByPeriodDataModel { ClientFIO = x.Key, TotalDiscount = x.Sum(y => y.DiscountAmount), FromPeriod = x.Min(y => y.DiscountDate), ToPeriod = x.Max(y => y.DiscountDate) }).OrderBy(x => x.ClientFIO)];
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
namespace CandyHouseContracts.AdapterContracts;
public interface IClientDiscountAdapter
{
ClientDiscountOperationResponse GetListByPeriod(DateTime fromDate, DateTime toDate);
ClientDiscountOperationResponse GetListByPeriodByClient(DateTime fromDate, DateTime toDate, string clientId);
ClientDiscountOperationResponse CalculateClientDiscount(DateTime date);
}

View File

@@ -0,0 +1,23 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface IEmployeeAdapter
{
EmployeeOperationResponse GetList(bool includeDeleted);
EmployeeOperationResponse GetPostList(string id, bool includeDeleted);
EmployeeOperationResponse GetListByBirthDate(DateTime fromDate, DateTime toDate, bool includeDeleted);
EmployeeOperationResponse GetListByEmploymentDate(DateTime fromDate, DateTime toDate, bool includeDeleted);
EmployeeOperationResponse GetElement(string data);
EmployeeOperationResponse RegisterEmployee(EmployeeBindingModel employeeModel);
EmployeeOperationResponse ChangeEmployeeInfo(EmployeeBindingModel employeeModel);
EmployeeOperationResponse RemoveEmployee(string id);
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
namespace CandyHouseContracts.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataProductHistoryPricesAsync(CancellationToken ct);
Task<ReportOperationResponse> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentProductHistoryPricesAsync(CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.ViewModels;
namespace CandyHouseContracts.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 BadRequest(string message) => BadRequest<ClientOperationResponse>(message);
public static ClientOperationResponse NotFound(string message) => NotFound<ClientOperationResponse>(message);
public static ClientOperationResponse InternalServerError(string message) => InternalServerError<ClientOperationResponse>(message);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.ViewModels;
namespace CandyHouseContracts.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ProductHistoryPricesViewModel> data) => OK<ReportOperationResponse, List<ProductHistoryPricesViewModel>>(data);
public static ReportOperationResponse OK(List<SaleViewModel> data) => OK<ReportOperationResponse, List<SaleViewModel>>(data);
public static ReportOperationResponse OK(List<ClientDiscountByPeriodViewModel> data) => OK<ReportOperationResponse, List<ClientDiscountByPeriodViewModel>>(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 CandyHouseContracts.Infrastructure;
using CandyHouseContracts.ViewModels;
namespace CandyHouseContracts.AdapterContracts.OperationResponses;
public class SaleOperationResponse : OperationResponse
{
public static SaleOperationResponse OK(List<SaleViewModel> data) => OK<SaleOperationResponse, List<SaleViewModel>>(data);
public static SaleOperationResponse OK(SaleViewModel data) => OK<SaleOperationResponse, SaleViewModel>(data);
public static SaleOperationResponse NoContent() => NoContent<SaleOperationResponse>();
public static SaleOperationResponse NotFound(string message) => NotFound<SaleOperationResponse>(message);
public static SaleOperationResponse BadRequest(string message) => BadRequest<SaleOperationResponse>(message);
public static SaleOperationResponse InternalServerError(string message) => InternalServerError<SaleOperationResponse>(message);
}

View File

@@ -0,0 +1,14 @@
namespace CandyHouseContracts.BindingModels;
public class ClientBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? PhoneNumber { get; set; }
public string? Email { get; set; }
public string? ConfigurationJson { get; set; }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,16 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.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,12 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IClientDiscountBusinessLogicContract
{
List<ClientDiscountDataModel> GetAllClientDiscountsByPeriod(DateTime fromDate, DateTime toDate);
List<ClientDiscountDataModel> GetAllClientDiscountsByPeriodByClient(DateTime fromDate, DateTime toDate, string clientId);
void CalculateClientDiscountByMonth(DateTime date);
}

View File

@@ -0,0 +1,22 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IEmployeeBusinessLogicContract
{
List<EmployeeDataModel> GetAllEmployees(bool onlyActive = true);
List<EmployeeDataModel> GetAllEmployeesByPost(string postId, bool onlyActive = true);
List<EmployeeDataModel> GetAllEmployeesByBirthDate(DateTime fromDate, DateTime toDate, bool onlyActive = true);
List<EmployeeDataModel> GetAllEmployeesByEmploymentDate(DateTime fromDate, DateTime toDate, bool onlyActive = true);
EmployeeDataModel GetEmployeeByData(string data);
void InsertEmployee(EmployeeDataModel employeeDataModel);
void UpdateEmployee(EmployeeDataModel employeeDataModel);
void DeleteEmployee(string id);
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,18 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IReportContract
{
Task<List<ProductHistoryPricesDataModel>> GetDataProductHistoryPricesAsync(CancellationToken ct);
Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<ClientDiscountByPeriodDataModel>> GetDataClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentProductHistoryPricesAsync(CancellationToken ct);
Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentClientDiscountByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,20 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface ISaleBusinessLogicContract
{
List<SaleDataModel> GetAllSalesByPeriod(DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByEmployeeByPeriod(string employeeId, DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByClientByPeriod(string clientId, DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByProductByPeriod(string productId, DateTime fromDate, DateTime toDate);
SaleDataModel GetSaleByData(string data);
void InsertSale(SaleDataModel saleDataModel);
void CancelSale(string id);
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Messages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CandyHouseBusinessLogic" />
<InternalsVisibleTo Include="CandyHouseDatabase" />
<InternalsVisibleTo Include="CandyHouseWebApi" />
<InternalsVisibleTo Include="CandyHouseTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Messages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Messages.resx</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,75 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Infrastructure.ClientConfigurations;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
using CandyHouseContracts.Mapper;
namespace CandyHouseContracts.DataModels;
internal class ClientDataModel(string id, string fio, string phoneNumber, string email, ClientConfiguration configuration) : IValidation
{
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string PhoneNumber { get; private set; } = phoneNumber;
public string Email { get; private set; } = email;
[AlternativeName("ConfigurationJson")]
[AlternativeName("Configuration")]
[PostProcessing(MappingCallMethodName = "ParseJson")]
public ClientConfiguration ConfigurationModel { get; private set; } = configuration;
public ClientDataModel() : this(string.Empty, string.Empty, string.Empty, string.Empty, null) { }
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["ValidationExceptionMessageNotAId"], "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, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
throw new ValidationException(localizer["ValidationExceptionMessageIncorrectPhoneNumber"]);
if (Email.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Email"));
if (!Regex.IsMatch(Email, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"))
throw new ValidationException(localizer["ValidationExceptionMessageIncorrectEmail"]);
if (ConfigurationModel is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "ConfigurationModel"));
if (ConfigurationModel!.BasicLevel <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "BasicLevel"));
}
private ClientConfiguration? ParseJson(string json)
{
var obj = JToken.Parse(json);
if (obj is not null)
{
return obj.Value<string>("Type") switch
{
nameof(SilverClientConfiguration) => JsonConvert.DeserializeObject<SilverClientConfiguration>(json)!,
nameof(GoldenClientConfiguration) => JsonConvert.DeserializeObject<GoldenClientConfiguration>(json)!,
_ => JsonConvert.DeserializeObject<ClientConfiguration>(json)!,
};
}
return null;
}
}

View File

@@ -0,0 +1,12 @@
namespace CandyHouseContracts.DataModels;
public class ClientDiscountByPeriodDataModel
{
public required string ClientFIO { get; set; }
public double TotalDiscount { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -0,0 +1,39 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ClientDiscountDataModel(string clientId, DateTime discountDate, double discountAmount) : IValidation
{
private readonly ClientDataModel? _client;
public string ClientId { get; private set; } = clientId;
public DateTime DiscountDate { get; private set; } = discountDate.ToUniversalTime();
public double DiscountAmount { get; private set; } = discountAmount;
public string ClientFIO => _client?.FIO ?? string.Empty;
public ClientDiscountDataModel(string clientId, DateTime discountDate, double discountAmount, ClientDataModel client) : this(clientId, discountDate, discountAmount)
{
_client = client;
}
public ClientDiscountDataModel() : this(string.Empty, DateTime.UtcNow, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (ClientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "EmployeeId"));
if (!ClientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "EmployeeId"));
if (DiscountAmount <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "DiscountAmount"));
}
}

View File

@@ -0,0 +1,64 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class EmployeeDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted) : IValidation
{
private readonly PostDataModel? _post;
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string PostId { get; private set; } = postId;
public DateTime BirthDate { get; private set; } = birthDate.ToUniversalTime();
public DateTime EmploymentDate { get; private set; } = employmentDate.ToUniversalTime();
public bool IsDeleted { get; private set; } = isDeleted;
public string PostName => _post?.PostName ?? string.Empty;
public EmployeeDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted, PostDataModel post) : this(id, fio, postId, birthDate, employmentDate, isDeleted)
{
_post = post;
}
public EmployeeDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate) : this(id, fio, postId, birthDate, employmentDate, false) { }
public EmployeeDataModel() : this(string.Empty, string.Empty, string.Empty, DateTime.UtcNow, DateTime.UtcNow) { }
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["ValidationExceptionMessageNotAId"], "Id"));
if (FIO.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "FIO"));
if (PostId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostId"));
if (!PostId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "PostId"));
if (BirthDate.Date > DateTime.Now.AddYears(-16).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)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsEmploymentDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
}
}

View File

@@ -0,0 +1,34 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ManufacturerDataModel(string id, string manufacturerName, string? prevManufacturerName, string? prevPrevManufacturerName) : IValidation
{
public string Id { get; private set; } = id;
public string ManufacturerName { get; private set; } = manufacturerName;
public string? PrevManufacturerName { get; private set; } = prevManufacturerName;
public string? PrevPrevManufacturerName { get; private set; } = prevPrevManufacturerName;
public ManufacturerDataModel() : this(string.Empty, string.Empty, null, null) { }
public ManufacturerDataModel(string id, string manufacturerName) : this(id, manufacturerName, null, null) { }
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["ValidationExceptionMessageNotAId"], "Id"));
if (ManufacturerName.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ManufacturerName"));
}
}

View File

@@ -0,0 +1,41 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Mapper;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class PostDataModel(string postId, string postName, PostType postType, double salary) : IValidation
{
[AlternativeName("PostId")]
public string Id { get; private set; } = postId;
public string PostName { get; private set; } = postName;
public PostType PostType { get; private set; } = postType;
public double Salary { get; private set; } = salary;
public PostDataModel() : this(string.Empty, string.Empty, PostType.None, 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["ValidationExceptionMessageNotAId"], "Id"));
if (PostName.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostName"));
if (PostType == PostType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "PostType"));
if (Salary <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Salary"));
}
}

View File

@@ -0,0 +1,60 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price, bool isDeleted) : IValidation
{
private readonly ManufacturerDataModel? _manufacturer;
public string Id { get; private set; } = id;
public string ProductName { get; private set; } = productName;
public ProductType ProductType { get; private set; } = productType;
public string ManufacturerId { get; private set; } = manufacturerId;
public double Price { get; private set; } = price;
public bool IsDeleted { get; private set; } = isDeleted;
public string ManufacturerName => _manufacturer?.ManufacturerName ?? string.Empty;
public ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price, bool isDeleted, ManufacturerDataModel manufacturer) : this(id, productName, productType, manufacturerId, price, isDeleted)
{
_manufacturer = manufacturer;
}
public ProductDataModel(string id, string productName, ProductType productType, string manufacturerId, double price) : this(id, productName, productType, manufacturerId, price, false) { }
public ProductDataModel() : this(string.Empty, string.Empty, ProductType.None, string.Empty, 0, 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["ValidationExceptionMessageNotAId"], "Id"));
if (ProductName.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductName"));
if (ProductType == ProductType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductType"));
if (ManufacturerId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ManufacturerId"));
if (!ManufacturerId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ManufacturerId"));
if (Price <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

View File

@@ -0,0 +1,40 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ProductHistoryDataModel(string productId, double oldPrice) : IValidation
{
private readonly ProductDataModel? _product;
public string ProductId { get; private set; } = productId;
public double OldPrice { get; private set; } = oldPrice;
public DateTime ChangeDate { get; private set; } = DateTime.UtcNow;
public string ProductName => _product?.ProductName ?? string.Empty;
public ProductHistoryDataModel(string productId, double oldPrice, DateTime changeDate, ProductDataModel product) : this(productId, oldPrice)
{
ChangeDate = changeDate;
_product = product;
}
public ProductHistoryDataModel() : this(string.Empty, 0, DateTime.UtcNow, null) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (ProductId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductId"));
if (!ProductId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ProductId"));
if (OldPrice <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "OldPrice"));
}
}

View File

@@ -0,0 +1,8 @@
namespace CandyHouseContracts.DataModels;
public class ProductHistoryPricesDataModel
{
public required string ProductName { get; set; }
public required List<string> Prices { get; set; }
}

View File

@@ -0,0 +1,111 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SaleDataModel : IValidation
{
private readonly ClientDataModel? _client;
private readonly EmployeeDataModel? _employee;
public string Id { get; private set; }
public string EmployeeId { get; private set; }
public string? ClientId { get; private set; }
public DateTime SaleDate { get; private set; } = DateTime.UtcNow;
public double Sum { get; private set; }
public DiscountType DiscountType { get; private set; }
public double Discount { get; private set; }
public bool IsCancel { get; private set; }
public List<SaleProductDataModel> Products { get; private set; }
public string ClientFIO => _client?.FIO ?? string.Empty;
public string EmployeeFIO => _employee?.FIO ?? string.Empty;
public SaleDataModel(string id, string employeeId, string? clientId, DiscountType discountType, bool isCancel, List<SaleProductDataModel> saleProducts)
{
Id = id;
EmployeeId = employeeId;
ClientId = clientId;
DiscountType = discountType;
IsCancel = isCancel;
Products = saleProducts;
var percent = 0.0;
foreach (DiscountType elem in Enum.GetValues<DiscountType>())
{
if ((elem & discountType) != 0)
{
switch (elem)
{
case DiscountType.None:
break;
case DiscountType.OnSale:
percent += 0.1;
break;
case DiscountType.RegularCustomer:
percent += 0.5;
break;
case DiscountType.Certificate:
percent += 0.3;
break;
}
}
}
Sum = Products?.Sum(x => x.Price * x.Count) ?? 0;
Discount = Sum * percent;
}
public SaleDataModel(string id, string employeeId, string? clientId, double sum, DiscountType discountType, double discount, bool isCancel, List<SaleProductDataModel> saleProducts, EmployeeDataModel employee, ClientDataModel? client) : this(id, employeeId, clientId, discountType, isCancel, saleProducts)
{
Sum = sum;
Discount = discount;
_employee = employee;
_client = client;
}
public SaleDataModel(string id, string employeeId, string? clientId, int discountType, List<SaleProductDataModel> products) : this(id, employeeId, clientId, (DiscountType)discountType, false, products) { }
public SaleDataModel() : this(string.Empty, string.Empty, null, DiscountType.None, false, new List<SaleProductDataModel>()) { }
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["ValidationExceptionMessageNotAId"], "Id"));
if (EmployeeId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "EmployeeId"));
if (!EmployeeId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "EmployeeId"));
if (!ClientId?.IsGuid() ?? !ClientId?.IsEmpty() ?? false)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ClientId"));
if (Sum <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Sum"));
if (Products is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "Products"));
if (Products.Count == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoProductsInSale"]);
Products.ForEach(x => x.Validate(localizer));
}
}

View File

@@ -0,0 +1,50 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SaleProductDataModel(string saleId, string productId, int count, double price) : IValidation
{
private readonly ProductDataModel? _product;
public string SaleId { get; private set; } = saleId;
public string ProductId { get; private set; } = productId;
public int Count { get; private set; } = count;
public double Price { get; private set; } = price;
public string ProductName => _product?.ProductName ?? string.Empty;
public SaleProductDataModel(string saleId, string productId, int count, double price, ProductDataModel product) : this(saleId, productId, count, price)
{
_product = product;
}
public SaleProductDataModel() : this(string.Empty, string.Empty, 0, 0) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (SaleId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SaleId"));
if (!SaleId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "SaleId"));
if (ProductId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IProductIdd"));
if (!ProductId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ProductId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if (Price <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Price"));
}
}

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