48 Commits

Author SHA1 Message Date
d7b6039323 lab 7 ready 2025-05-29 21:40:02 +04:00
b13ae5a0f6 dodep 2025-05-29 21:28:34 +04:00
6daf8317e0 fix 6 2025-05-29 21:27:03 +04:00
8dc9cc8995 lab 2025-05-29 21:15:56 +04:00
6134ab544d lab5 fix 2025-05-29 21:12:17 +04:00
f96a4906d0 lab 5 dodep 2025-05-29 20:52:45 +04:00
b9fbb33468 lab5-ready 2025-05-23 10:13:06 +04:00
7d7c2bd522 lab6 hard fix 2025-05-18 21:30:25 +04:00
813564aece lab6 hard ready 2025-05-18 21:24:47 +04:00
a21f0e3d88 Merge branch 'refs/heads/lab-5' into lab-5-hard
# Conflicts:
#	CandyHouseSolution/CandyHouseBusinessLogic/CandyHouseBusinessLogic.csproj
#	CandyHouseSolution/CandyHouseBusinessLogic/Implementations/ClientBusinessLogicContract.cs
#	CandyHouseSolution/CandyHouseBusinessLogic/Implementations/SalaryBusinessLogicContract.cs
#	CandyHouseSolution/CandyHouseBusinessLogic/Implementations/SaleBusinessLogicContract.cs
#	CandyHouseSolution/CandyHouseContracts/BindingModels/PostBindingModel.cs
#	CandyHouseSolution/CandyHouseContracts/CandyHouseContracts.csproj
#	CandyHouseSolution/CandyHouseContracts/DataModels/EmployeeDataModel.cs
#	CandyHouseSolution/CandyHouseContracts/DataModels/PostDataModel.cs
#	CandyHouseSolution/CandyHouseContracts/Enums/PostType.cs
#	CandyHouseSolution/CandyHouseContracts/Infrastructure/OperationResponse.cs
#	CandyHouseSolution/CandyHouseContracts/StoragesContracts/IEmployeeStorageContract.cs
#	CandyHouseSolution/CandyHouseContracts/ViewModels/PostViewModel.cs
#	CandyHouseSolution/CandyHouseSolution.sln
#	CandyHouseSolution/CandyHouseTests/BusinessLogicContractsTests/PostBusinessLogicContractTests.cs
#	CandyHouseSolution/CandyHouseTests/BusinessLogicContractsTests/ProductBusinessLogicContractTests.cs
#	CandyHouseSolution/CandyHouseTests/BusinessLogicContractsTests/SalaryBusinessLogicContractTests.cs
#	CandyHouseSolution/CandyHouseTests/BusinessLogicContractsTests/SaleBusinessLogicContractTests.cs
#	CandyHouseSolution/CandyHouseTests/CandyHouseTests.csproj
#	CandyHouseSolution/CandyHouseTests/DataModelTests/PostDataModelTests.cs
#	CandyHouseSolution/CandyHouseTests/DataModelTests/ProductDataModelTests.cs
#	CandyHouseSolution/CandyHouseTests/Infrastructure/CandyHouseDbContextExtensions.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/BaseStorageContractTest.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/ClientStorageContractTests.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/EmployeeStorageContractTests.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/ProductStorageContractTests.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/SalaryStorageContractTests.cs
#	CandyHouseSolution/CandyHouseTests/StoragesContractsTests/SaleStorageContractTests.cs
#	CandyHouseSolution/CandyHouseTests/WebApiControllersTests/PostControllerTests.cs
#	CandyHouseSolution/CandyHouseTests/WebApiControllersTests/ProductControllerTests.cs
#	CandyHouseSolution/CandyHouseTests/WebApiControllersTests/SalaryControllerTests.cs
#	CandyHouseSolution/CandyHouseTests/WebApiControllersTests/SaleControllerTests.cs
#	CandyHouseSolution/CandyHouseTests/appsettings.json
#	CandyHouseSolution/CandyHouseWebApi/Adapters/PostAdapter.cs
#	CandyHouseSolution/CandyHouseWebApi/CandyHouseWebApi.csproj
#	CandyHouseSolution/CandyHouseWebApi/Controllers/SalesController.cs
#	CandyHouseSolution/CandyHouseWebApi/Program.cs
#	CandyHouseSolution/CandyHouseWebApi/appsettings.json
2025-05-18 20:32:58 +04:00
235b06a497 4-hard 2025-05-18 01:41:05 +04:00
3501f2a724 test fix 2025-04-15 15:51:05 +04:00
00a8846f33 some fix 2025-04-15 15:20:49 +04:00
86881f7317 lab3-ready 2025-04-15 02:40:51 +04:00
d51c616862 fix 2025-04-15 01:47:02 +04:00
4518d36016 lab2-ready 2025-04-15 01:25:50 +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
e0f0af6754 Add mock operations 2025-03-26 21:51:51 +04:00
cec7b9240e Ready full 2025-03-13 16:04:10 +04:00
dbde99c7eb Wip beda 2025-03-13 15:40:49 +04:00
d2f7afae63 lab-3 ready 2025-03-13 13:38:00 +04:00
fa6b1b64a2 lab2 hard ready 2025-03-11 21:44:01 +04:00
bdb510f098 lab-2 merged to lab2-hard 2025-03-11 21:18:14 +04:00
75c5ceabc0 red-line 2025-03-11 21:11:14 +04:00
514f2adefb lab1-hard merged to lab2-hard-no-logic 2025-03-11 20:45:10 +04:00
6ad09c5ff2 Merge branch 'refs/heads/lab-1-hard' into lab-2-hard-no-logic
# Conflicts:
#	CandyHouseSolution/CandyHouseBase/DataModels/StorageDataModel.cs
2025-03-11 20:37:07 +04:00
807ac64a9f fix 2025-03-11 20:36:00 +04:00
ffaeb985e5 Revert "lab1-hard"
This reverts commit b4819145
2025-03-11 20:27:46 +04:00
7b71b14c71 Merge branch 'refs/heads/lab-1-hard' into lab-2-hard-no-logic
# Conflicts:
#	CandyHouseSolution/CandyHouseBase/CandyHouseBase.csproj
2025-03-11 20:26:20 +04:00
b481914563 lab1-hard 2025-02-27 12:01:44 +04:00
805a1d0322 RED LINE UPDATE 2025-02-27 02:25:39 +04:00
b7c31860e1 lab2 green 2025-02-27 02:14:25 +04:00
79cdd4b5d7 fail tests commit 2025-02-26 23:49:16 +04:00
310 changed files with 34736 additions and 1118 deletions

7
.gitignore vendored
View File

@@ -10,7 +10,7 @@
*.user
*.userosscache
*.sln.docstates
*.idea
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
@@ -247,9 +247,9 @@ orleans.codegen.cs
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# Since there are multiple workflows, uncomment next line to ignore bower_ingredients
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
#bower_ingredients/
# RIA/Silverlight projects
Generated_Code/
@@ -414,3 +414,4 @@ FodyWeavers.xsd
# Built Visual Studio Code Extensions
*.vsix
/.idea/

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props"
Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')"/>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5DF70B46-31F7-4D15-8C60-52C0A0A364F0}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CandyHouseBase</RootNamespace>
<AssemblyName>CandyHouseBase</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System"/>
<Reference Include="System.Core"/>
<Reference Include="System.Data"/>
<Reference Include="System.Xml"/>
</ItemGroup>
<ItemGroup>
<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\ValidationException.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Infrastructure\IValidation.cs" />
<Compile Include="Program.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets"/>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</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,46 +0,0 @@
using System;
using System.Collections.Generic;
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 OrderId { 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 orderId, string pekarId, StatusType statusType)
{
Id = id;
CustomerName = customerName;
OrderDate = orderDate;
TotalAmount = totalAmount;
DiscountAmount = discountAmount;
OrderId = orderId;
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 (OrderId.IsEmpty()) throw new ValidationException("Field OrderId is empty");
if (!OrderId.IsGuid()) throw new ValidationException("OrderId must be a GUID");
}
}
}

View File

@@ -1,42 +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 List<ProductDataModel> ProductsItems { get; private set; }
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,58 +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;
private set
{
if (!name.IsEmpty()) OldName = name;
name = value.Trim();
}
}
public string Description
{
get => description;
private 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(string id, string name, string description, List<IngredientDataModel> ingredients)
{
Id = id;
Name = name;
Description = description;
IngredientsItems = ingredients;
}
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,9 +0,0 @@
using System;
namespace CandyHouseBase.Exceptions
{
public class ValidationException : Exception
{
public ValidationException(string message) : base(message) { }
}
}

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,7 +0,0 @@
namespace CandyHouseBase.Infrastructure
{
public interface IValidation
{
void Validate();
}
}

View File

@@ -1,9 +0,0 @@
namespace CandyHouseBase
{
public class Program
{
public static void Main(string[] args)
{
}
}
}

View File

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

View File

@@ -0,0 +1,80 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseBusinessLogic.Implementations;
internal class CustomerBusinessLogicContract(ICustomerStorageContract customerStorage, IStringLocalizer<Messages> localizer, ILogger<CustomerBusinessLogicContract> logger) : ICustomerBusinessLogicContract
{
private readonly ILogger<CustomerBusinessLogicContract> _logger = logger;
private readonly ICustomerStorageContract _customerStorage = customerStorage;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<CustomerDataModel> GetAllCustomers()
{
_logger.LogInformation("Fetching all customers");
return _customerStorage.GetList();
}
public CustomerDataModel GetCustomerByData(string data)
{
_logger.LogInformation("Fetching customer by data={data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
CustomerDataModel? result;
if (data.IsGuid())
{
result = _customerStorage.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
else if (data.StartsWith("+7"))
{
result = _customerStorage.GetElementByPhone(data) ?? throw new ElementNotFoundException(data, _localizer);
}
else if (data.Contains("@"))
{
result = _customerStorage.GetElementByEmail(data) ?? throw new ElementNotFoundException(data, _localizer);
}
else
{
result = _customerStorage.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return result;
}
public void InsertCustomer(CustomerDataModel customerDataModel)
{
_logger.LogInformation("Inserting customer: {json}", JsonSerializer.Serialize(customerDataModel));
ArgumentNullException.ThrowIfNull(customerDataModel);
customerDataModel.Validate(_localizer);
_customerStorage.AddElement(customerDataModel);
}
public void UpdateCustomer(CustomerDataModel customerDataModel)
{
_logger.LogInformation("Updating customer: {json}", JsonSerializer.Serialize(customerDataModel));
ArgumentNullException.ThrowIfNull(customerDataModel);
customerDataModel.Validate(_localizer);
_customerStorage.UpdateElement(customerDataModel);
}
public void DeleteCustomer(string id)
{
_logger.LogInformation("Deleting customer with id={id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_customerStorage.DeleteElement(id);
}
}

View File

@@ -0,0 +1,100 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseBusinessLogic.Implementations;
internal class IngredientBusinessLogicContract(IIngredientStorageContract ingredientStorage, IStringLocalizer<Messages> localizer, ILogger<IngredientBusinessLogicContract> logger) : IIngredientBusinessLogicContract
{
private readonly ILogger<IngredientBusinessLogicContract> _logger = logger;
private readonly IIngredientStorageContract _ingredientStorage = ingredientStorage;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<IngredientDataModel> GetAllIngredients(bool onlyActive = true)
{
_logger.LogInformation("GetAllIngredients params: {onlyActive}", onlyActive);
return _ingredientStorage.GetList(onlyActive, null);
}
public List<IngredientDataModel> GetAllIngredientsByManufacturer(string manufacturerId, bool onlyActive = true)
{
_logger.LogInformation("GetAllIngredients params: {manufacturerId}, {onlyActive}", manufacturerId, onlyActive);
if (manufacturerId.IsEmpty())
{
throw new ArgumentNullException(nameof(manufacturerId));
}
if (!manufacturerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ManufacturerId"));
}
return _ingredientStorage.GetList(onlyActive, manufacturerId);
}
public List<IngredientHistoryDataModel> GetIngredientHistory(string ingredientId)
{
_logger.LogInformation("GetIngredientHistory for {ingredientId}", ingredientId);
if (ingredientId.IsEmpty())
{
throw new ArgumentNullException(nameof(ingredientId));
}
if (!ingredientId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
}
return _ingredientStorage.GetHistoryByIngredientId(ingredientId);
}
public IngredientDataModel GetIngredientByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
IngredientDataModel? result;
if (data.IsGuid())
{
result = _ingredientStorage.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
else
{
result = _ingredientStorage.GetElementByName(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return result;
}
public void InsertIngredient(IngredientDataModel ingredientDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(ingredientDataModel));
ArgumentNullException.ThrowIfNull(ingredientDataModel);
ingredientDataModel.Validate(_localizer);
_ingredientStorage.AddElement(ingredientDataModel);
}
public void UpdateIngredient(IngredientDataModel ingredientDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(ingredientDataModel));
ArgumentNullException.ThrowIfNull(ingredientDataModel);
ingredientDataModel.Validate(_localizer);
_ingredientStorage.UpdateElement(ingredientDataModel);
}
public void DeleteIngredient(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"));
}
_ingredientStorage.DeleteElement(id);
}
}

View File

@@ -0,0 +1,79 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseBusinessLogic.Implementations;
internal class ManufacturerBusinessLogicContract(IManufacturerStorageContract manufacturerStorage, IStringLocalizer<Messages> localizer, ILogger<ManufacturerBusinessLogicContract> logger) : IManufacturerBusinessLogicContract
{
private readonly ILogger<ManufacturerBusinessLogicContract> _logger = logger;
private readonly IManufacturerStorageContract _manufacturerStorage = manufacturerStorage;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<ManufacturerDataModel> GetAllManufacturers()
{
_logger.LogInformation("GetAllManufacturers");
return _manufacturerStorage.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 _manufacturerStorage.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
// Сначала ищем по ManufacturerName
var manufacturer = _manufacturerStorage.GetElementByName(data);
if (manufacturer != null)
{
return manufacturer;
}
// Если не нашли по ManufacturerName, ищем по PrevManufacturerName
manufacturer = _manufacturerStorage.GetList()
.FirstOrDefault(m => m.PrevManufacturerName != null && m.PrevManufacturerName.Equals(data, StringComparison.OrdinalIgnoreCase));
return manufacturer ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertManufacturer(ManufacturerDataModel manufacturerDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(manufacturerDataModel));
ArgumentNullException.ThrowIfNull(manufacturerDataModel);
manufacturerDataModel.Validate(_localizer);
_manufacturerStorage.AddElement(manufacturerDataModel);
}
public void UpdateManufacturer(ManufacturerDataModel manufacturerDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(manufacturerDataModel));
ArgumentNullException.ThrowIfNull(manufacturerDataModel);
manufacturerDataModel.Validate(_localizer);
_manufacturerStorage.UpdateElement(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"));
}
_manufacturerStorage.DeleteElement(id);
}
}

View File

@@ -0,0 +1,109 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
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.GetPostHistory(postId);
}
public List<PostDataModel> GetPostHistory(string postId)
{
_logger.LogInformation("GetPostHistory for {postId}", postId);
if (postId.IsEmpty())
{
throw new ArgumentNullException(nameof(postId));
}
if (!postId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "PostId"));
}
return _postStorageContract.GetPostHistory(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.UpdateElement(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.DeleteElement(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,126 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
using DocumentFormat.OpenXml.Office2010.Excel;
namespace CandyHouseBusinessLogic.Implementations;
/// <summary>
/// Реализация бизнес-логики
/// </summary>
internal class ProductBusinessLogicContract : IProductBusinessLogicContract
{
private readonly ILogger<ProductBusinessLogicContract> _logger;
private readonly IProductStorageContract _productStorageContract;
private readonly IStringLocalizer<Messages> _localizer;
public ProductBusinessLogicContract(IProductStorageContract productStorage, IStringLocalizer<Messages> localizer, ILogger<ProductBusinessLogicContract> logger)
{
_logger = logger;
_productStorageContract = productStorage;
_localizer = localizer;
}
public List<ProductDataModel> GetAllProducts(bool onlyActive = true)
{
_logger.LogInformation("Fetching all products with onlyActive={onlyActive}", onlyActive);
return _productStorageContract.GetList(onlyActive: onlyActive);
}
public ProductDataModel GetProductById(string id)
{
_logger.LogInformation("Fetching sale product by id={id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
return _productStorageContract.GetElementById(id) ?? throw new ElementNotFoundException(id, _localizer);
}
public void InsertProduct(ProductDataModel productDataModel)
{
_logger.LogInformation("Inserting sale product: {json}", JsonSerializer.Serialize(productDataModel));
ArgumentNullException.ThrowIfNull(productDataModel);
productDataModel.Validate(_localizer);
_productStorageContract.AddElement(productDataModel);
}
public void UpdateProduct(ProductDataModel productDataModel)
{
_logger.LogInformation("Updating sale product: {json}", JsonSerializer.Serialize(productDataModel));
ArgumentNullException.ThrowIfNull(productDataModel);
productDataModel.Validate(_localizer);
_productStorageContract.UpdateElement(productDataModel);
}
public void DeleteProduct(string id)
{
_logger.LogInformation("Deleting sale product with id={id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_productStorageContract.DeleteElement(id);
}
public List<ProductIngredientDataModel> GetIngredientsByProductId(string productId)
{
_logger.LogInformation("Fetching ingredients for sale product with id={saleProductId}", productId);
if (productId.IsEmpty())
{
throw new ArgumentNullException(nameof(productId));
}
if (!productId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ProductId"));
}
return _productStorageContract.GetIngredientsByProductId(productId);
}
public void AddIngredientToProduct(ProductIngredientDataModel productIngredient)
{
_logger.LogInformation("Adding ingredient to product: {json}", JsonSerializer.Serialize(productIngredient));
ArgumentNullException.ThrowIfNull(productIngredient);
productIngredient.Validate(_localizer);
_productStorageContract.AddIngredientToProduct(productIngredient);
}
public void RemoveIngredientFromProduct(string ingredientId, string productId)
{
_logger.LogInformation("Removing ingredient {ingredientId} from product {productId}", ingredientId, productId);
if (ingredientId.IsEmpty())
{
throw new ArgumentNullException(nameof(ingredientId));
}
if (!ingredientId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
}
if (productId.IsEmpty())
{
throw new ArgumentNullException(nameof(productId));
}
if (!productId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "ProductId"));
}
_productStorageContract.RemoveIngredientFromProduct(ingredientId, productId);
}
}

View File

@@ -0,0 +1,623 @@
using DocumentFormat.OpenXml.Wordprocessing;
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;
using System.Globalization;
namespace CandyHouseBusinessLogic.Implementations;
internal class ReportContract : IReportContract
{
private readonly IIngredientStorageContract _ingredientStorageContract;
private readonly IStringLocalizer<Messages> _localizer;
private readonly ILogger _logger;
private readonly BaseWordBuilder _baseWordBuilder;
private readonly ISaleStorageContract _saleStorageContract;
private readonly ISalaryStorageContract _salaryStorageContract;
private readonly IStorageContract _storageContract;
private readonly ISupplyStorageContract _supplyStorageContract;
private readonly BaseExcelBuilder _baseExcelBuilder;
private readonly BasePdfBuilder _basePdfBuilder;
private readonly string[] _documentHeader;
private readonly string[] _tableHeader;
private readonly string[] _supplyTableHeader;
private readonly string[] _movementTableHeader;
private readonly string[] _stockTableHeader;
public ReportContract(IIngredientStorageContract ingredientStorageContract, ISaleStorageContract saleStorageContract, IProductStorageContract productStorageContract, ISalaryStorageContract salaryStorageContract, IStorageContract storageContract, ISupplyStorageContract supplyStorageContract, BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, IStringLocalizer<Messages> localizer, ILogger logger)
{
_ingredientStorageContract = ingredientStorageContract;
_localizer = localizer;
_logger = logger;
_baseWordBuilder = baseWordBuilder;
_saleStorageContract = saleStorageContract;
_salaryStorageContract = salaryStorageContract;
_storageContract = storageContract;
_supplyStorageContract = supplyStorageContract;
_baseExcelBuilder = baseExcelBuilder;
_basePdfBuilder = basePdfBuilder;
_documentHeader = [_localizer["DocumentDocCaptionIngredient"], _localizer["DocumentDocCaptionIngredientHistory"], _localizer["DocumentDocCaptionOldPrice"]];
_tableHeader = [_localizer["DocumentExcelCaptionDate"], _localizer["DocumentExcelCaptionSum"],
_localizer["DocumentExcelCaptionDiscount"], _localizer["DocumentExcelCaptionWorker"], _localizer["DocumentExcelCaptionProduct"], _localizer["DocumentExcelCaptionCount"]];
_supplyTableHeader = [_localizer["DocumentDocSupplyCaptionDate"],
_localizer["DocumentDocSupplyCaptionStorage"],
_localizer["DocumentDocSupplyCaptionIngredient"],
_localizer["DocumentDocSupplyCaptionCount"]
];
_movementTableHeader = [_localizer["DocumentExcelMovementCaptionDate"],
_localizer["DocumentExcelMovementCaptionOperation"],
_localizer["DocumentExcelMovementCaptionIngredient"],
_localizer["DocumentExcelMovementCaptionCount"],
_localizer["DocumentExcelMovementCaptionStorage"]
];
_stockTableHeader = [_localizer["DocumentPdfStockCaptionIngredient"],
_localizer["DocumentPdfStockCaptionCount"]
];
}
public async Task<List<IngredientWithHistoryDataModel>> GetDataIngredientsWithHistoryAsync(CancellationToken ct)
{
_logger.LogInformation("Get data IngredientsWithHistory");
return await GetDataByIngredientsAsync(ct);
}
public async Task<Stream> CreateDocumentIngredientsWithHistoryAsync(CancellationToken ct)
{
_logger.LogInformation("Create report IngredientsWithHistory");
// Получаем данные
var data = await GetDataIngredientsWithHistoryAsync(ct);
if (!data.Any())
{
throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
}
// Формируем строки таблицы
var tableRows = new List<string[]>
{
_documentHeader // ["DocumentDocCaptionIngredient", "DocumentDocCaptionIngredientHistory", "DocumentDocCaptionOldPrice"]
};
foreach (var ingredient in data.OrderBy(c => c.IngredientName))
{
if (ingredient.History.Any())
{
// Сортируем историю по убыванию даты
var sortedHistory = ingredient.History.OrderByDescending(h => h.ChangeDate).ToList();
// Добавляем первую строку с названием комплектующей
var firstHistory = sortedHistory.First();
tableRows.Add(new[]
{
ingredient.IngredientName,
firstHistory.ChangeDate.ToLocalTime().ToString("dd.MM.yyyy HH:mm:ss"),
firstHistory.OldPrice.ToString("N2", CultureInfo.CurrentCulture)
});
// Добавляем остальные записи истории
foreach (var history in sortedHistory.Skip(1))
{
tableRows.Add(new[]
{
"", // Пустая ячейка для названия комплектующей
history.ChangeDate.ToLocalTime().ToString("dd.MM.yyyy HH:mm:ss"),
history.OldPrice.ToString("N2", CultureInfo.CurrentCulture)
});
}
}
else
{
// Если истории нет, добавляем строку с локализованным текстом
tableRows.Add(new[]
{
ingredient.IngredientName,
_localizer["NotFoundDataMessage"], // Локализованный текст "Нет данных"
"" // Пустая ячейка для старой цены
});
}
}
// Создаём документ с таблицей
return _baseWordBuilder
.AddHeader(_localizer["DocumentDocHeader"]) // "Продукты по производителям" или аналог
.AddParagraph(string.Format(_localizer["DocumentDocSubHeader"], DateTime.Now.ToLocalTime().ToShortDateString()))
.AddTable(new[] { 4000, 3000, 2000 }, tableRows) // Устанавливаем ширину колонок
.Build();
}
public async Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Creating SalesByPeriod report from {DateStart} to {DateFinish}", dateStart, dateFinish);
var salesData = await GetDataBySalesAsync(dateStart, dateFinish, ct)
?? throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
if (!salesData.Any())
{
_logger.LogInformation("No sales records found from {DateStart} to {DateFinish}", dateStart, dateFinish);
throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
}
var tableRows = new List<string[]>
{
_tableHeader // ["DocumentExcelCaptionDate", "DocumentExcelCaptionSum", ..., "DocumentExcelCaptionCount"]
};
foreach (var sale in salesData)
{
if (sale.Product == null)
{
_logger.LogWarning("Product data missing for sale ID {SaleId}", sale.Id);
continue;
}
var productInfo = sale.Product;
var productName = productInfo.ProductName;
tableRows.Add(new[]
{
sale.SaleDate.ToLocalTime().ToShortDateString(),
sale.Sum.ToString("N2", CultureInfo.CurrentCulture),
sale.Discount.ToString("N2", CultureInfo.CurrentCulture),
sale.WorkerFIO ?? _localizer["NotFoundDataMessage"],
productName,
"1"
});
}
tableRows.Add(new[]
{
_localizer["DocumentExcelCaptionTotal"], // "Всего"
salesData.Sum(x => x.Sum).ToString("N2", CultureInfo.CurrentCulture),
salesData.Sum(x => x.Discount).ToString("N2", CultureInfo.CurrentCulture),
"", "", ""
});
return _baseExcelBuilder
.AddHeader(_localizer["DocumentExcelHeader"], startIndex: 0, count: 6) // "Продажи за период"
.AddParagraph(string.Format(_localizer["DocumentExcelSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()), columnIndex: 0)
.AddTable(
columnsWidths: new[] { 15, 10, 10, 20, 30, 10 },
data: tableRows
)
.Build();
}
public async Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report SalaryByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataBySalaryAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
return _basePdfBuilder
.AddHeader(_localizer["DocumentPdfHeader"]) // "Зарплатная ведомость"
.AddParagraph(string.Format(_localizer["DocumentPdfSubHeader"], dateStart.ToLocalTime().ToShortDateString(), dateFinish.ToLocalTime().ToShortDateString()))
.AddPieChart(_localizer["DocumentPdfDiagramCaption"], [.. data.Select(x => (x.WorkerFIO, x.TotalSalary))]) // "Начисления"
.Build();
}
public Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data SalesByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataBySalesAsync(dateStart, dateFinish, ct);
}
public Task<List<WorkerSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data SalaryByPeriod from {dateStart} to {dateFinish}", dateStart, dateFinish);
return GetDataBySalaryAsync(dateStart, dateFinish, ct);
}
private async Task<List<IngredientWithHistoryDataModel>> GetDataByIngredientsAsync(CancellationToken ct)
{
_logger.LogInformation("Starting GetDataByIngredientsAsync");
// Получаем все комплектующие
var ingredients = await _ingredientStorageContract.GetListAsync(ct);
_logger.LogInformation("Retrieved {Count} ingredients from storage", ingredients.Count);
// Формируем список комплектующих с их историей (последовательно)
var result = new List<IngredientWithHistoryDataModel>();
foreach (var ingredient in ingredients)
{
_logger.LogInformation("Processing ingredient {IngredientName} with ID {IngredientId}", ingredient.IngredientName, ingredient.Id);
// Получаем историю изменений для текущей комплектующей асинхронно
var history = await _ingredientStorageContract.GetHistoryByIngredientIdAsync(ingredient.Id, ct);
_logger.LogInformation("Retrieved {HistoryCount} history records for ingredient {IngredientName}", history.Count, ingredient.IngredientName);
// Логируем данные, которые передаём в IngredientWithHistoryDataModel
_logger.LogInformation("Creating IngredientWithHistoryDataModel for {IngredientName}. ManufacturerName: {ManufacturerName}, CurrentPrice: {Price}, History is null: {HistoryIsNull}",
ingredient.IngredientName, ingredient.ManufacturerName, ingredient.Price, history == null);
// Создаём объект для отчёта
var ingredientWithHistory = new IngredientWithHistoryDataModel(
ingredientName: ingredient.IngredientName,
currentPrice: ingredient.Price,
manufacturerName: ingredient.ManufacturerName,
history: history
);
// Вызываем валидацию
try
{
ingredientWithHistory.Validate(_localizer);
_logger.LogInformation("Validation passed for ingredient {IngredientName}", ingredient.IngredientName);
}
catch (ValidationException ex)
{
_logger.LogError(ex, "Validation failed for ingredient {IngredientName}: {Message}", ingredient.IngredientName, ex.Message);
throw;
}
result.Add(ingredientWithHistory);
}
_logger.LogInformation("All ingredients processed, {Count} ingredients processed", result.Count);
return result.OrderBy(x => x.IngredientName).ToList();
}
private async Task<List<SaleDataModel>> GetDataBySalesAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return [.. (await _saleStorageContract.GetListAsync(dateStart, dateFinish, ct)).OrderBy(x => x.SaleDate)];
}
private async Task<List<WorkerSalaryByPeriodDataModel>> GetDataBySalaryAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
if (dateStart.IsDateNotOlder(dateFinish))
{
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
return (await _salaryStorageContract.GetListAsync(dateStart, dateFinish, ct))
.GroupBy(x => x.WorkerId)
.Select(x => new WorkerSalaryByPeriodDataModel
{
WorkerFIO = x.First().WorkerFIO,
TotalSalary = x.Sum(y => y.Salary),
FromPeriod = x.Min(y => y.SalaryDate),
ToPeriod = x.Max(y => y.SalaryDate)
})
.OrderBy(x => x.WorkerFIO)
.ToList();
}
public async Task<List<SupplyInformationDataModel>> GetDataSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data Supply from {dateStart} to {dateFinish}", dateStart, dateFinish);
if (dateStart.IsDateNotOlder(dateFinish))
{
_logger.LogError("Invalid date range: start date {DateStart} is not older than finish date {DateFinish}", dateStart, dateFinish);
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
var supplies = await _supplyStorageContract.GetListAsync(dateStart, dateFinish, ct);
var result = new List<SupplyInformationDataModel>();
if (supplies != null)
{
foreach (var supply in supplies)
{
var supplyInfo = new SupplyInformationDataModel
{
SupplyId = supply.Id,
SupplyDate = supply.DeliveryDate,
StorageId = supply.StorageId,
Ingredients = supply.Ingredients.Select(c => new SupplyIngredientReportDataModel(
supplyId: c.SupplyId,
ingredientId: c.IngredientId,
count: c.Count
)).ToList()
};
try
{
supplyInfo.Validate(_localizer);
_logger.LogInformation("Validation passed for supply {SupplyId}", supplyInfo.SupplyId);
}
catch (ValidationException ex)
{
_logger.LogError(ex, "Validation failed for supply {SupplyId}: {Message}", supplyInfo.SupplyId, ex.Message);
throw;
}
result.Add(supplyInfo);
}
}
return result.OrderBy(x => x.SupplyDate).ToList();
}
public async Task<Stream> CreateDocumentSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report Supply from {dateStart} to {dateFinish}", dateStart, dateFinish);
var supplies = await GetDataSupplyAsync(dateStart, dateFinish, ct)
?? throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
if (!supplies.Any())
{
_logger.LogInformation("No supply records found from {dateStart} to {dateFinish}", dateStart, dateFinish);
throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
}
var tableRows = new List<string[]>
{
_supplyTableHeader // ["DocumentDocSupplyCaptionDate", "DocumentDocSupplyCaptionStorage", "DocumentDocSupplyCaptionIngredient", "DocumentDocSupplyCaptionCount"]
};
// Группируем комплектующие по поставкам
var groupedSupplies = supplies
.OrderBy(s => s.SupplyDate)
.GroupBy(s => new { s.SupplyId, s.SupplyDate, s.StorageId })
.ToList();
for (int groupIndex = 0; groupIndex < groupedSupplies.Count; groupIndex++)
{
var group = groupedSupplies[groupIndex];
var supply = group.First();
var storage = await _storageContract.GetByIdAsync(supply.StorageId, ct);
if (storage == null)
{
_logger.LogWarning("Storage with ID {StorageId} not found for supply {SupplyId}", supply.StorageId, supply.SupplyId);
}
// Получаем все комплектующие для данной поставки
var ingredients = group.SelectMany(s => s.Ingredients).ToList();
for (int i = 0; i < ingredients.Count; i++)
{
var ingredient = ingredients[i];
var comp = await _ingredientStorageContract.GetElementByIdAsync(ingredient.IngredientId, ct);
if (comp == null)
{
_logger.LogWarning("Ingredient with ID {IngredientId} not found for supply {SupplyId}", ingredient.IngredientId, supply.SupplyId);
}
// Если это первая строка для данной поставки, показываем дату и склад
// Для остальных строк оставляем эти колонки пустыми
string dateCell = i == 0 ? supply.SupplyDate.ToLocalTime().ToString("d", CultureInfo.CurrentCulture) : "";
string storageCell = i == 0 ? (storage?.Address ?? _localizer["NotFoundDataMessage"]) : "";
tableRows.Add(new[]
{
dateCell,
storageCell,
comp?.IngredientName ?? ingredient.IngredientId,
ingredient.Count.ToString()
});
}
// Добавляем пустую строку как разделитель между поставками (если это не последняя поставка)
if (groupIndex < groupedSupplies.Count - 1)
{
tableRows.Add(new[] { "", "", "", "" });
}
}
// Добавляем итоговую строку
tableRows.Add(new[]
{
_localizer["DocumentExcelCaptionTotal"], // "Итого"
"",
"",
supplies.Sum(s => s.Ingredients.Sum(c => c.Count)).ToString()
});
return _baseWordBuilder
.AddHeader(_localizer["DocumentSupplyHeader"]) // "Отчёт по поставке"
.AddParagraph(string.Format(_localizer["DocumentSupplySubHeader"], dateStart.ToLocalTime().ToString("d", CultureInfo.CurrentCulture), dateFinish.ToLocalTime().ToString("d", CultureInfo.CurrentCulture)))
.AddTable(new[] { 2000, 2000, 3000, 1500 }, tableRows)
.Build();
}
public async Task<List<IngredientMoveDataModel>> GetDataIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data IngredientMovement from {dateStart} to {dateFinish}", dateStart, dateFinish);
if (dateStart.IsDateNotOlder(dateFinish))
{
_logger.LogError("Invalid date range: start date {DateStart} is not older than finish date {DateFinish}", dateStart, dateFinish);
throw new IncorrectDatesException(dateStart, dateFinish, _localizer);
}
var movements = new List<IngredientMoveDataModel>();
// Получаем поставки
var supplies = await _supplyStorageContract.GetListAsync(dateStart, dateFinish, ct);
if (supplies != null)
{
foreach (var supply in supplies)
{
foreach (var ingredient in supply.Ingredients)
{
var comp = await _ingredientStorageContract.GetElementByIdAsync(ingredient.IngredientId, ct);
if (comp == null)
{
_logger.LogWarning("Ingredient with ID {IngredientId} not found for supply {SupplyId}", ingredient.IngredientId, supply.Id);
}
movements.Add(new IngredientMoveDataModel
{
Date = supply.DeliveryDate,
OperationType = _localizer["DocumentMovementOperationArrival"].Value, // "Приход"
IngredientId = ingredient.IngredientId,
IngredientName = comp?.IngredientName ?? ingredient.IngredientId,
Count = ingredient.Count,
StorageId = supply.StorageId
});
}
}
}
// Получаем продажи
var sales = await _saleStorageContract.GetListAsync(dateStart, dateFinish, ct);
if (sales != null)
{
foreach (var sale in sales)
{
if (sale.Product?.Ingredients != null)
{
foreach (var ingredient in sale.Product.Ingredients)
{
var comp = await _ingredientStorageContract.GetElementByIdAsync(ingredient.IngredientId, ct);
if (comp == null)
{
_logger.LogWarning("Ingredient with ID {IngredientId} not found for sale {SaleId}", ingredient.IngredientId, sale.Id);
}
movements.Add(new IngredientMoveDataModel
{
Date = sale.SaleDate,
OperationType = _localizer["DocumentMovementOperationDeparture"].Value, // "Уход"
IngredientId = ingredient.IngredientId,
IngredientName = comp?.IngredientName ?? ingredient.IngredientId,
Count = ingredient.Count,
StorageId = null
});
}
}
}
}
return movements.OrderBy(m => m.Date).ToList();
}
public async Task<Stream> CreateDocumentIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report IngredientMovement from {dateStart} to {dateFinish}", dateStart, dateFinish);
var movements = await GetDataIngredientMovementAsync(dateStart, dateFinish, ct)
?? throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
if (!movements.Any())
{
_logger.LogInformation("No movement records found from {dateStart} to {dateFinish}", dateStart, dateFinish);
throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
}
// Логируем содержимое movements
_logger.LogInformation("Found {Count} movements", movements.Count);
foreach (var movement in movements)
{
_logger.LogInformation("Movement: Date={Date}, OperationType={OperationType}, IngredientId={IngredientId}, IngredientName={IngredientName}, Count={Count}, StorageId={StorageId}",
movement.Date, movement.OperationType, movement.IngredientId, movement.IngredientName, movement.Count, movement.StorageId);
}
var tableRows = new List<string[]>
{
_movementTableHeader // // ["DocumentExcelMovementCaptionDate", "DocumentExcelMovementCaptionOperation", "DocumentExcelMovementCaptionIngredient", "DocumentExcelMovementCaptionCount", "DocumentExcelMovementCaptionStorage"]
};
foreach (var movement in movements)
{
var storage = movement.StorageId != null ? await _storageContract.GetByIdAsync(movement.StorageId, ct) : null;
if (storage == null && movement.StorageId != null)
{
_logger.LogWarning("Storage with ID {StorageId} not found for movement of ingredient {IngredientId}", movement.StorageId, movement.IngredientId);
}
tableRows.Add(new[]
{
movement.Date.ToLocalTime().ToString("d", CultureInfo.CurrentCulture),
movement.OperationType, // "Приход" или "Уход" (локализуется в данных)
movement.IngredientName,
movement.Count.ToString(),
storage?.Address ?? _localizer["NotFoundDataMessage"]
});
}
// Вычисляем остаток: приходы увеличивают, уходы уменьшают
var totalBalance = movements
.Sum(m => m.OperationType == _localizer["DocumentMovementOperationArrival"].Value ? m.Count : -m.Count);
if (totalBalance < 0)
{
_logger.LogWarning("Negative balance detected: {TotalBalance}. Possible data inconsistency.", totalBalance);
throw new InvalidOperationException(_localizer["NegativeBalanceMessage"]);
}
tableRows.Add(new[]
{
_localizer["DocumentMovementCaptionTotal"], // "Итого (остаток)"
"",
"",
totalBalance.ToString(),
""
});
// Логируем содержимое tableRows
_logger.LogInformation("Table rows count: {Count}", tableRows.Count);
foreach (var row in tableRows)
{
_logger.LogInformation("Row: {Row}", string.Join(", ", row));
}
return _baseExcelBuilder
.AddHeader(_localizer["DocumentMovementHeader"], 0, 5) // "Движение комплектующих"
.AddParagraph(string.Format(_localizer["DocumentMovementSubHeader"], dateStart.ToLocalTime().ToString("d", CultureInfo.CurrentCulture), dateFinish.ToLocalTime().ToString("d", CultureInfo.CurrentCulture)), 0)
.AddTable(new[] { 15, 15, 25, 10, 20 }, tableRows)
.Build();
}
public async Task<List<StorageIngredientDataModel>> GetDataStorageStockAsync(string storageId, CancellationToken ct)
{
_logger.LogInformation("Get data StorageStock for storage {storageId}", storageId);
var ingredients = await _storageContract.GetIngredientsOnStorageAsync(storageId, ct);
return ingredients?.OrderBy(c => c.IngredientId).ToList() ?? new List<StorageIngredientDataModel>();
}
public async Task<Stream> CreateDocumentStorageStockAsync(string storageId, CancellationToken ct)
{
_logger.LogInformation("Create report StorageStock for storage {storageId}", storageId);
var ingredients = await GetDataStorageStockAsync(storageId, ct)
?? throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
if (!ingredients.Any())
{
_logger.LogInformation("No stock records found for storage {storageId}", storageId);
throw new InvalidOperationException(_localizer["NotFoundDataMessage"]);
}
var storage = await _storageContract.GetByIdAsync(storageId, ct);
if (storage == null)
{
_logger.LogWarning("Storage with ID {StorageId} not found", storageId);
}
// Предварительно загружаем все компоненты для оптимизации
var ingredientIds = ingredients.Select(c => c.IngredientId).Distinct().ToList();
var ingredientDict = new Dictionary<string, IngredientDataModel>();
foreach (var ingredientId in ingredientIds)
{
var comp = await _ingredientStorageContract.GetElementByIdAsync(ingredientId, ct);
if (comp != null)
{
ingredientDict[ingredientId] = comp;
}
else
{
_logger.LogWarning("Ingredient with ID {IngredientId} not found for storage {StorageId}", ingredientId, storageId);
}
}
var chartData = ingredients.Select(c => (
ingredientDict.TryGetValue(c.IngredientId, out var comp) ? comp.IngredientName : c.IngredientId,
(double)c.Count
)).ToList();
return _basePdfBuilder
.AddHeader(_localizer["DocumentStockHeader"]) // "Комплектующие на складе"
.AddParagraph(string.Format(_localizer["DocumentStockSubHeader"], storage?.Address ?? _localizer["NotFoundDataMessage"]))
.AddPieChart(_localizer["DocumentStockChartCaption"], chartData) // "Количество комплектующих"
.Build();
}
}

View File

@@ -0,0 +1,148 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Infrastructure.PostConfigurations;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseBusinessLogic.Implementations;
internal class SalaryBusinessLogicContract(ISalaryStorageContract salaryStorageContract, ISaleStorageContract saleStorageContract, IPostStorageContract postStorageContract, IWorkerStorageContract workerStorageContract, IStringLocalizer<Messages> localizer, ILogger logger, IConfigurationSalary сonfiguration) : ISalaryBusinessLogicContract
{
private readonly ILogger _logger = logger;
private readonly ISalaryStorageContract _salaryStorageContract = salaryStorageContract;
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
private readonly IPostStorageContract _postStorageContract = postStorageContract;
private readonly IWorkerStorageContract _workerStorageContract = workerStorageContract;
private readonly IConfigurationSalary _salaryConfiguration = сonfiguration;
private readonly IStringLocalizer<Messages> _localizer = localizer;
private readonly object _lockObject = new();
public List<SalaryDataModel> GetAllSalariesByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSalaries params: {fromDate}, {toDate}",
fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _salaryStorageContract.GetList(fromDate, toDate);
}
public List<SalaryDataModel> GetAllSalariesByPeriodByWorker(DateTime fromDate, DateTime toDate, string workerId)
{
var fromUtc = fromDate.ToUniversalTime();
var toUtc = toDate.ToUniversalTime();
if (fromUtc.IsDateNotOlder(toUtc))
{
throw new IncorrectDatesException(fromUtc, toUtc, _localizer);
}
if (workerId.IsEmpty())
{
throw new ArgumentNullException(nameof(workerId));
}
if (!workerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
}
_logger.LogInformation("GetAllSalaries params: {fromDate}, {toDate}, {workerId}", fromUtc, toUtc, workerId);
return _salaryStorageContract.GetList(fromUtc, toUtc, workerId);
}
public void CalculateSalaryByMonth(DateTime date)
{
_logger.LogInformation("CalculateSalaryByMounth: {date}", date);
var startDate = new DateTime(date.Year, date.Month, 1, 0, 0, 0, DateTimeKind.Utc);
var finishDate = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), 23, 59, 59, 999, DateTimeKind.Utc);
var workers = _workerStorageContract.GetList();
foreach (var worker in workers)
{
var sales = _saleStorageContract.GetList(startDate, finishDate, workerId: worker.Id);
var post = _postStorageContract.GetElementById(worker.PostId);
if (post == null)
{
_logger.LogError("Post with ID {PostId} not found for worker {WorkerId}", worker.PostId, worker.Id);
throw new ElementNotFoundException(worker.PostId, _localizer);
}
var salary = post.ConfigurationModel switch
{
null => 0,
CashierPostConfiguration cpc => CalculateSalaryForCashier(sales, startDate, finishDate, cpc),
SupervisorPostConfiguration spc => CalculateSalaryForSupervisor(startDate, finishDate, spc),
PostConfiguration pc => pc.Rate,
};
_logger.LogDebug("The employee {workerId} was paid a salary of {salary}", worker.Id, salary);
_salaryStorageContract.AddElement(new SalaryDataModel(worker.Id, finishDate, salary));
}
}
private double CalculateSalaryForCashier(List<SaleDataModel> sales, DateTime startDate, DateTime finishDate, CashierPostConfiguration config)
{
var tasks = new List<Task>();
var calcPercent = 0.0;
var semaphore = new SemaphoreSlim(_salaryConfiguration.MaxConcurrentThreads); // Ограничение потоков
// Создаём задачи для каждого дня
for (var date = startDate; date <= finishDate; date = date.AddDays(1))
{
var currentDate = date; // Сохраняем дату для каждой задачи
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync(); // Ждём доступный слот
try
{
var salesInDay = sales.Where(x => x.SaleDate.Date == currentDate.Date && !x.IsCancel).ToArray();
if (salesInDay.Length > 0)
{
lock (_lockObject)
{
calcPercent += (salesInDay.Sum(x => x.Sum) / salesInDay.Length) * _salaryConfiguration.CashierSalePercent;
}
}
}
finally
{
semaphore.Release(); // Освобождаем слот
}
}));
}
// Расчёт бонуса в отдельной задаче
var calcBonusTask = Task.Run(() =>
{
return sales.Where(x => x.Sum > _salaryConfiguration.ExtraSaleSum && !x.IsCancel)
.Sum(x => x.Sum) * _salaryConfiguration.CashierBonusForExtraSales;
});
try
{
Task.WaitAll(tasks.ToArray()); // Ждём завершения всех задач для процентов
var bonus = calcBonusTask.Result; // Получаем результат бонуса
return config.Rate + calcPercent + bonus;
}
catch (AggregateException agEx)
{
foreach (var ex in agEx.InnerExceptions)
{
_logger.LogError(ex, "Error in the cashier payroll process");
}
return 0;
}
}
private double CalculateSalaryForSupervisor(DateTime startDate, DateTime finishDate, SupervisorPostConfiguration config)
{
try
{
return config.Rate + _salaryConfiguration.SupervisorTrendPremium * _workerStorageContract.GetWorkerTrend(startDate, finishDate); // Из глобальных настроек
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in the supervisor payroll process");
return 0;
}
}
}

View File

@@ -0,0 +1,170 @@
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;
internal class SaleBusinessLogicContract(ISaleStorageContract saleStorageContract, IStorageContract storageStorageContract, IStringLocalizer<Messages> localizer, ILogger logger) : ISaleBusinessLogicContract{
private readonly ILogger _logger = logger;
private readonly ISaleStorageContract _saleStorageContract = saleStorageContract;
private readonly IStorageContract _storageStorageContract = storageStorageContract;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<SaleDataModel> GetAllSalesByPeriod(DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {fromDate}, {toDate}",
fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _saleStorageContract.GetList(fromDate, toDate);
}
public List<SaleDataModel> GetAllSalesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {workerId}, {fromDate}, {toDate}", workerId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (workerId.IsEmpty())
{
throw new ArgumentNullException(nameof(workerId));
}
if (!workerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
}
return _saleStorageContract.GetList(fromDate, toDate, workerId:
workerId);
}
public List<SaleDataModel> GetAllSalesByCustomerByPeriod(string customerId,
DateTime fromDate, DateTime toDate)
{
_logger.LogInformation("GetAllSales params: {customerId}, {fromDate}, {toDate}", customerId, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
if (customerId.IsEmpty())
{
throw new ArgumentNullException(nameof(customerId));
}
if (!customerId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "CustomerId"));
}
return _saleStorageContract.GetList(fromDate, toDate, customerId:
customerId);
}
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);
if (!_storageStorageContract.CheckAndWriteOffСomponentsFromStorage(saleDataModel))
{
throw new InsufficientStockException(_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);
}
public double CalculateSalesRevenue(DateTime fromDate, DateTime toDate)
{
if (fromDate > toDate)
throw new ValidationException("fromDate cannot be later than toDate");
_logger.LogInformation("Calculating sales revenue from {fromDate} to {toDate}", fromDate, toDate);
var sales = GetAllSalesByPeriod(fromDate, toDate);
if (sales == null || !sales.Any())
{
_logger.LogWarning("No sales found for the period {fromDate} to {toDate}", fromDate, toDate);
return 0.0;
}
double totalRevenue = 0.0;
object lockObject = new object();
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
try
{
Parallel.ForEach(sales, parallelOptions, sale =>
{
if (!sale.IsCancel)
{
double saleRevenue = sale.Sum - sale.Discount;
lock (lockObject)
{
totalRevenue += saleRevenue;
}
}
});
}
catch (AggregateException ex)
{
_logger.LogError(ex, "Error occurred while calculating sales revenue");
throw new StorageException(ex, _localizer);
}
_logger.LogInformation("Total sales revenue: {totalRevenue}", totalRevenue);
return totalRevenue;
}
}

View File

@@ -0,0 +1,203 @@
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
using System.Text.Json;
namespace CandyHouseBusinessLogic.Implementations;
internal class StorageBusinessLogicContract : IStorageBusinessLogicContract
{
private readonly IStorageContract _storageContract;
private readonly ISupplyStorageContract _supplyStorageContract;
private readonly ILogger<StorageBusinessLogicContract> _logger;
private readonly IStringLocalizer<Messages> _localizer;
public StorageBusinessLogicContract(IStorageContract storageContract, ISupplyStorageContract supplyStorageContract, IStringLocalizer<Messages> localizer, ILogger<StorageBusinessLogicContract> logger)
{
_storageContract = storageContract ?? throw new ArgumentNullException(nameof(storageContract));
_supplyStorageContract = supplyStorageContract ?? throw new ArgumentNullException(nameof(supplyStorageContract));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_localizer = localizer;
}
public List<StorageDataModel> GetAllStorages()
{
var storages = _storageContract.GetAllStorages();
return storages;
}
public void CreateStorage(string id, string address, int count, List<StorageIngredientDataModel> ingredients)
{
if (string.IsNullOrEmpty(id))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "id"));
if (string.IsNullOrEmpty(address))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "address"));
// Проверка существования склада с таким ID
if (_storageContract.GetStorageById(id) != null)
throw new ElementExistsException("Id", id, _localizer);
var storage = new StorageDataModel(id, address, count, ingredients ?? new List<StorageIngredientDataModel>());
storage.Validate(_localizer);
_storageContract.AddStorage(storage);
}
public void UpdateStorage(StorageDataModel storage)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(storage));
ArgumentNullException.ThrowIfNull(storage);
storage.Validate(_localizer);
var existingStorage = _storageContract.GetStorageById(storage.Id);
if (existingStorage == null)
throw new ElementNotFoundException($"Storage with ID {storage.Id} not found", _localizer);
_storageContract.UpdateStorage(storage);
}
public void AddIngredientToStorage(string storageId, StorageIngredientDataModel ingredient)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
if (ingredient == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "ingredient"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
ingredient.Validate(_localizer);
var existingIngredient = _storageContract.GetIngredientOnStorage(storageId, ingredient.IngredientId);
var updatedCount = existingIngredient?.Count + ingredient.Count ?? ingredient.Count;
var updatedIngredient = new StorageIngredientDataModel(storageId, ingredient.IngredientId, updatedCount);
_storageContract.AddOrUpdateIngredientOnStorage(updatedIngredient);
}
public void UpdateIngredientCount(string storageId, string ingredientId, int newCount)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
if (string.IsNullOrEmpty(ingredientId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "ingredientId"));
if (newCount < 0)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageLessOrEqualZero"], "newCount"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
_storageContract.UpdateIngredientCount(storageId, ingredientId, newCount);
}
public int GetIngredientCountFromStorage(string storageId, string ingredientId)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
if (string.IsNullOrEmpty(ingredientId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "ingredientId"));
var ingredient = _storageContract.GetIngredientOnStorage(storageId, ingredientId);
if (ingredient == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "ingredient"));
return ingredient.Count;
}
public List<StorageIngredientDataModel> GetIngredientsFromStorage(string storageId)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
return _storageContract.GetIngredientsOnStorage(storageId);
}
public StorageDataModel GetStorageById(string storageId)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ElementNotFoundException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"), _localizer);
return storage;
}
public Dictionary<string, int> CalculateRemainingIngredients(string storageId)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
_logger.LogInformation("Calculating remaining ingredients for storage {storageId}", storageId);
var remainingIngredients = new Dictionary<string, int>();
try
{
var supplyTask = Task.Run(() =>
{
var supplies = _supplyStorageContract.GetSuppliesByStorageId(storageId);
var suppliedIngredients = new Dictionary<string, int>();
foreach (var supply in supplies)
{
if (supply.Ingredients != null)
{
foreach (var ingredient in supply.Ingredients)
{
if (suppliedIngredients.ContainsKey(ingredient.IngredientId))
suppliedIngredients[ingredient.IngredientId] += ingredient.Count;
else
suppliedIngredients[ingredient.IngredientId] = ingredient.Count;
}
}
}
return suppliedIngredients;
});
var writeOffTask = Task.Run(() =>
{
return _storageContract.GetWrittenOffIngredientsByStorageId(storageId);
});
Task.WhenAll(supplyTask, writeOffTask).GetAwaiter().GetResult();
var suppliedIngredients = supplyTask.Result;
var writtenOffIngredients = writeOffTask.Result;
foreach (var ingredient in suppliedIngredients)
{
int writtenOffCount = writtenOffIngredients.ContainsKey(ingredient.Key) ? writtenOffIngredients[ingredient.Key] : 0;
int remainingCount = ingredient.Value - writtenOffCount;
if (remainingCount > 0)
{
remainingIngredients[ingredient.Key] = remainingCount;
}
}
_logger.LogInformation("Remaining ingredients calculated for storage {storageId}: {count} ingredients", storageId, remainingIngredients.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error occurred while calculating remaining ingredients for storage {storageId}", storageId);
throw new StorageException(ex, _localizer);
}
return remainingIngredients;
}
}

View File

@@ -0,0 +1,174 @@
using DocumentFormat.OpenXml.VariantTypes;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Resources;
using CandyHouseContracts.StoragesContracts;
namespace CandyHouseBusinessLogic.Implementations;
internal class SupplyBusinessLogicContract : ISupplyBusinessLogicContract
{
private readonly ISupplyStorageContract _supplyStorageContract;
private readonly IStorageContract _storageContract;
private readonly IStringLocalizer<Messages> _localizer;
public SupplyBusinessLogicContract(ISupplyStorageContract supplyStorageContract, IStorageContract storageContract, IStringLocalizer<Messages> localizer)
{
_supplyStorageContract = supplyStorageContract ?? throw new ArgumentNullException(nameof(supplyStorageContract));
_storageContract = storageContract ?? throw new ArgumentNullException(nameof(storageContract));
_localizer = localizer;
}
public void CreateSupply(SupplyDataModel supply)
{
if (supply == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "supply"));
// Проверка существования поставки с таким ID
if (_supplyStorageContract.GetSupplyById(supply.Id) != null)
throw new ElementExistsException("Id", supply.Id, _localizer);
supply.Validate(_localizer);
var storage = _storageContract.GetStorageById(supply.StorageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
foreach (var ingredient in supply.Ingredients)
{
ingredient.Validate(_localizer);
}
_supplyStorageContract.AddSupply(supply);
try
{
foreach (var ingredient in supply.Ingredients)
{
var storageIngredient = _storageContract.GetIngredientOnStorage(supply.StorageId, ingredient.IngredientId)
?? new StorageIngredientDataModel(supply.StorageId, ingredient.IngredientId, 0);
var updatedIngredient = new StorageIngredientDataModel(
storageIngredient.StorageId,
storageIngredient.IngredientId,
storageIngredient.Count + ingredient.Count
);
_storageContract.AddOrUpdateIngredientOnStorage(updatedIngredient);
}
}
catch (Exception ex)
{
_supplyStorageContract.RemoveSupply(supply.Id);
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageUnexpectedError"], ex));
}
}
public List<SupplyDataModel> GetAllSupplies()
{
var supplies = _supplyStorageContract.GetAllSupplies();
if (supplies is null || supplies.Count == 0)
{
throw new ElementNotFoundException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "supplyId"), _localizer);
}
return supplies;
}
public SupplyDataModel GetSupplyById(string supplyId)
{
if (string.IsNullOrEmpty(supplyId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "supplyId"));
var supply = _supplyStorageContract.GetSupplyById(supplyId);
if (supply == null)
throw new ElementNotFoundException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "supply"), _localizer);
return supply;
}
public List<SupplyDataModel> GetSuppliesByStorageId(string storageId)
{
if (string.IsNullOrEmpty(storageId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "storageId"));
var storage = _storageContract.GetStorageById(storageId);
if (storage == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "storage"));
return _supplyStorageContract.GetSuppliesByStorageId(storageId);
}
public void AddOrUpdateIngredientInSupply(SupplyIngredientDataModel ingredient)
{
if (ingredient == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "ingredient"));
ingredient.Validate(_localizer);
_supplyStorageContract.AddOrUpdateIngredientInSupply(ingredient);
}
public void UpdateIngredientCountInSupply(string supplyId, string ingredientId, int newCount)
{
if (string.IsNullOrEmpty(supplyId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "supplyId"));
if (string.IsNullOrEmpty(ingredientId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "ingredientId"));
if (newCount <= 0)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageLessOrEqualZero"], "newCount"));
var supply = _supplyStorageContract.GetSupplyById(supplyId);
if (supply == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "supply"));
_supplyStorageContract.UpdateIngredientCountInSupply(supplyId, ingredientId, newCount);
}
public void RemoveIngredientFromSupply(string supplyId, string ingredientId)
{
if (string.IsNullOrEmpty(supplyId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "supplyId"));
if (string.IsNullOrEmpty(ingredientId))
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageEmptyField"], "ingredientId"));
var supply = _supplyStorageContract.GetSupplyById(supplyId);
if (supply == null)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotInitialized"], "supply"));
_supplyStorageContract.RemoveIngredientFromSupply(supplyId, ingredientId);
}
public int CalculateSuppliedIngredientsCount(DateTime fromDate, DateTime toDate)
{
if (fromDate > toDate)
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageDeliveryDateInFuture"], fromDate.ToShortDateString()));
var supplies = _supplyStorageContract.GetSuppliesByPeriod(fromDate, toDate);
if (supplies == null || !supplies.Any())
{
return 0;
}
int totalCount = 0;
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount };
try
{
Parallel.ForEach(supplies, parallelOptions, supply =>
{
if (supply.Ingredients != null)
{
int supplyCount = supply.Ingredients.Sum(c => c.Count);
Interlocked.Add(ref totalCount, supplyCount);
}
});
}
catch (AggregateException ex)
{
throw new StorageException(ex, _localizer);
}
return totalCount;
}
}

View File

@@ -0,0 +1,101 @@
using CandyHouseContracts.BusinessLogicsContracts;
using CandyHouseContracts.DataModels;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.StoragesContracts;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseBusinessLogic.Implementations;
internal class WorkerBusinessLogicContract(IWorkerStorageContract workerStorage, IStringLocalizer<Messages> localizer, ILogger<WorkerBusinessLogicContract> logger) : IWorkerBusinessLogicContract
{
private readonly ILogger<WorkerBusinessLogicContract> _logger = logger;
private readonly IWorkerStorageContract _workerStorage = workerStorage;
private readonly IStringLocalizer<Messages> _localizer = localizer;
public List<WorkerDataModel> GetAllWorkers(bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}", onlyActive);
return _workerStorage.GetList(onlyActive, null, null, null, null, null);
}
public List<WorkerDataModel> GetAllWorkersByPost(string postId, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {postId}, {onlyActive}", postId, onlyActive);
if (postId.IsEmpty())
{
throw new ArgumentNullException(nameof(postId));
}
if (!postId.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "PostId"));
}
return _workerStorage.GetList(onlyActive, postId, null, null, null, null);
}
public List<WorkerDataModel> GetAllWorkersByBirthDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _workerStorage.GetList(onlyActive, null, fromDate, toDate, null, null);
}
public List<WorkerDataModel> GetAllWorkersByEmploymentDate(DateTime fromDate, DateTime toDate, bool onlyActive = true)
{
_logger.LogInformation("GetAllWorkers params: {onlyActive}, {fromDate}, {toDate}", onlyActive, fromDate, toDate);
if (fromDate.IsDateNotOlder(toDate))
{
throw new IncorrectDatesException(fromDate, toDate, _localizer);
}
return _workerStorage.GetList(onlyActive, null, null, null, fromDate, toDate);
}
public WorkerDataModel GetWorkerByData(string data)
{
_logger.LogInformation("Get element by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _workerStorage.GetElementById(data) ?? throw new ElementNotFoundException(data, _localizer);
}
return _workerStorage.GetElementByFIO(data) ?? throw new ElementNotFoundException(data, _localizer);
}
public void InsertWorker(WorkerDataModel workerDataModel)
{
_logger.LogInformation("New data: {json}", JsonSerializer.Serialize(workerDataModel));
ArgumentNullException.ThrowIfNull(workerDataModel);
workerDataModel.Validate(_localizer);
_workerStorage.AddElement(workerDataModel);
}
public void UpdateWorker(WorkerDataModel workerDataModel)
{
_logger.LogInformation("Update data: {json}", JsonSerializer.Serialize(workerDataModel));
ArgumentNullException.ThrowIfNull(workerDataModel);
workerDataModel.Validate(_localizer);
_workerStorage.UpdateElement(workerDataModel);
}
public void DeleteWorker(string id)
{
_logger.LogInformation("Delete by id: {id}", id);
if (id.IsEmpty())
{
throw new ArgumentNullException(nameof(id));
}
if (!id.IsGuid())
{
throw new ValidationException(string.Format(_localizer["ValidationExceptionMessageNotAId"], "Id"));
}
_workerStorage.DeleteElement(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,86 @@
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.Value;
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;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
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,22 @@

using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface IIngredientAdapter
{
IngredientOperationResponse GetList(bool includeDeleted);
IngredientOperationResponse GetManufacturerList(string id, bool includeDeleted);
IngredientOperationResponse GetHistory(string id);
IngredientOperationResponse GetElement(string data);
IngredientOperationResponse RegisterIngredient(IngredientBindingModel productModel);
IngredientOperationResponse ChangeIngredientInfo(IngredientBindingModel productModel);
IngredientOperationResponse RemoveIngredient(string id);
}

View File

@@ -0,0 +1,17 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface ICustomerAdapter
{
CustomerOperationResponse GetList();
CustomerOperationResponse GetElement(string data);
CustomerOperationResponse RegisterCustomer(CustomerBindingModel customerModel);
CustomerOperationResponse ChangeCustomerInfo(CustomerBindingModel customerModel);
CustomerOperationResponse RemoveCustomer(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,24 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.AdapterContracts;
public interface IProductAdapter
{
ProductOperationResponse GetList(bool onlyActive = true);
ProductOperationResponse GetElement(string data);
ProductOperationResponse RegisterProduct(ProductBindingModel productModel);
ProductOperationResponse ChangeProductInfo(ProductBindingModel productModel);
ProductOperationResponse RemoveProduct(string id);
ProductOperationResponse GetIngredientsList(string id);
ProductOperationResponse AddIngredient(string productId, IngredientBindingModel ingredient);
ProductOperationResponse RemoveIngredient(string productId, string ingredientId);
}

View File

@@ -0,0 +1,32 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.DataModels;
using System.Threading.Tasks;
namespace CandyHouseContracts.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataIngredientsWithHistoryAsync(CancellationToken ct); // Переименовал
Task<ReportOperationResponse> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentIngredientsWithHistoryAsync(CancellationToken ct); // Переименовал
Task<ReportOperationResponse> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
// таски для харда
Task<ReportOperationResponse> GetDataSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataStorageStockAsync(string storageId, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentStorageStockAsync(string storageId, CancellationToken ct);
}

View File

@@ -0,0 +1,13 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface ISalaryAdapter
{
SalaryOperationResponse GetListByPeriod(DateTime fromDate, DateTime toDate);
SalaryOperationResponse GetListByPeriodByWorker(DateTime fromDate, DateTime toDate, string workerId);
SalaryOperationResponse CalculateSalary(DateTime date);
}

View File

@@ -0,0 +1,15 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface ISaleAdapter
{
SaleOperationResponse GetList(DateTime fromDate, DateTime toDate);
SaleOperationResponse GetWorkerList(string id, DateTime fromDate, DateTime toDate);
SaleOperationResponse GetCustomerList(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,13 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface IStorageAdapter
{
StorageOperationResponse GetList();
StorageOperationResponse GetElement(string id);
StorageOperationResponse RegisterStorage(StorageBindingModel storageModel);
StorageOperationResponse UpdateStorage(StorageBindingModel storageModel);
StorageOperationResponse AddOrUpdateIngredient(StorageIngredientBindingModel ingredientModel);
}

View File

@@ -0,0 +1,15 @@
using CandyHouseContracts.AdapterContracts.OperationResponses;
using CandyHouseContracts.BindingModels;
namespace CandyHouseContracts.AdapterContracts;
public interface ISupplyAdapter
{
SupplyOperationResponse GetList();
SupplyOperationResponse GetElement(string id);
SupplyOperationResponse RegisterSupply(SupplyBindingModel supplyModel);
SupplyOperationResponse GetSuppliesByStorage(string storageId);
SupplyOperationResponse AddOrUpdateIngredient(SupplyIngredientBindingModel ingredientModel);
SupplyOperationResponse UpdateIngredientCount(string supplyId, string ingredientId, int newCount);
SupplyOperationResponse RemoveIngredient(string supplyId, string ingredientId);
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.ViewModels;
namespace CandyHouseContracts.AdapterContracts.OperationResponses;
public class IngredientOperationResponse : OperationResponse
{
public static IngredientOperationResponse OK(List<IngredientViewModel> data) => OK<IngredientOperationResponse, List<IngredientViewModel>>(data);
public static IngredientOperationResponse OK(List<IngredientHistoryViewModel> data) => OK<IngredientOperationResponse, List<IngredientHistoryViewModel>>(data);
public static IngredientOperationResponse OK(IngredientViewModel data) => OK<IngredientOperationResponse, IngredientViewModel>(data);
public static IngredientOperationResponse NoContent() => NoContent<IngredientOperationResponse>();
public static IngredientOperationResponse NotFound(string message) => NotFound<IngredientOperationResponse>(message);
public static IngredientOperationResponse BadRequest(string message) => BadRequest<IngredientOperationResponse>(message);
public static IngredientOperationResponse InternalServerError(string message) => InternalServerError<IngredientOperationResponse>(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,28 @@
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<IngredientViewModel> data)
=> OK<ProductOperationResponse, List<IngredientViewModel>>(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,30 @@
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CandyHouseContracts.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<IngredientWithHistoryViewModel> data) => OK<ReportOperationResponse, List<IngredientWithHistoryViewModel>>(data); // Новый метод
public static ReportOperationResponse OK(Stream data, string fileName) => OK<ReportOperationResponse, Stream>(data, fileName);
public static ReportOperationResponse OK(List<SaleViewModel> data) => OK<ReportOperationResponse, List<SaleViewModel>>(data);
public static ReportOperationResponse OK(List<WorkerSalaryByPeriodViewModel> data) => OK<ReportOperationResponse, List<WorkerSalaryByPeriodViewModel>>(data);
public static ReportOperationResponse OK(List<SupplyInformationViewModel> data) => OK<ReportOperationResponse, List<SupplyInformationViewModel>>(data);
public static ReportOperationResponse OK(List<IngredientMoveViewModel> data) => OK<ReportOperationResponse, List<IngredientMoveViewModel>>(data);
public static ReportOperationResponse OK(List<StorageIngredientViewModel> data) => OK<ReportOperationResponse, List<StorageIngredientViewModel>>(data);
public static ReportOperationResponse BadRequest(string message) => BadRequest<ReportOperationResponse>(message);
public static ReportOperationResponse InternalServerError(string message) => InternalServerError<ReportOperationResponse>(message);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
namespace CandyHouseContracts.BindingModels;
public class CustomerBindingModel
{
public string? Id { get; set; }
public string? FIO { get; set; }
public string? Phone { get; set; }
public string? Email { get; set; }
public double DiscountSize { get; set; }
}

View File

@@ -0,0 +1,16 @@
using CandyHouseContracts.Enums;
namespace CandyHouseContracts.BindingModels;
public class IngredientBindingModel
{
public string? Id { get; set; }
public string? IngredientName { get; set; }
public string? IngredientType { get; set; }
public string? ManufacturerId { get; set; }
public double Price { 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,16 @@
using CandyHouseContracts.Enums;
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 string? ConfigurationJson { get; set; }
}

View File

@@ -0,0 +1,17 @@
using CandyHouseContracts.Enums;
namespace CandyHouseContracts.BindingModels;
public class ProductBindingModel
{
public string? Id { get; set; }
public string? ProductName { get; set; }
public int? Count { get; set; }
public double? TotalPrice { get; set; }
public List<ProductIngredientBindingModel>? Ingredients { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace CandyHouseContracts.BindingModels;
public class ProductIngredientBindingModel
{
public string? ProductId { get; set; }
public string? IngredientId { get; set; }
public int Count { get; set; }
}

View File

@@ -0,0 +1,17 @@
using CandyHouseContracts.Enums;
using CandyHouseContracts.ViewModels;
namespace CandyHouseContracts.BindingModels;
public class SaleBindingModel
{
public string? Id { get; set; }
public string? WorkerId { get; set; }
public string? CustomerId { get; set; }
public int DiscountType { get; set; }
public ProductBindingModel? Product { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace CandyHouseContracts.BindingModels;
public class StorageBindingModel
{
public string? Id { get; set; }
public required string Address { get; set; }
public int Count { get; set; }
public List<StorageIngredientBindingModel> Ingredients { get; set; } = [];
}

View File

@@ -0,0 +1,8 @@
namespace CandyHouseContracts.BindingModels;
public class StorageIngredientBindingModel
{
public required string StorageId { get; set; }
public required string IngredientId { get; set; }
public int Count { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace CandyHouseContracts.BindingModels;
public class SupplyBindingModel
{
public string? Id { get; set; }
public required string StorageId { get; set; }
public DateTime DeliveryDate { get; set; }
public int Count { get; set; }
public List<SupplyIngredientBindingModel> Ingredients { get; set; } = [];
}

View File

@@ -0,0 +1,8 @@
namespace CandyHouseContracts.BindingModels;
public class SupplyIngredientBindingModel
{
public required string SupplyId { get; set; }
public required string IngredientId { get; set; }
public int Count { get; set; }
}

View File

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

View File

@@ -0,0 +1,14 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IIngredientBusinessLogicContract
{
List<IngredientDataModel> GetAllIngredients(bool onlyActive = true);
List<IngredientDataModel> GetAllIngredientsByManufacturer(string manufacturerId, bool onlyActive = true);
List<IngredientHistoryDataModel> GetIngredientHistory(string ingredientId);
IngredientDataModel GetIngredientByData(string data);
void InsertIngredient(IngredientDataModel ingredientDataModel);
void UpdateIngredient(IngredientDataModel ingredientDataModel);
void DeleteIngredient(string id);
}

View File

@@ -0,0 +1,12 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface ICustomerBusinessLogicContract
{
List<CustomerDataModel> GetAllCustomers();
CustomerDataModel GetCustomerByData(string data);
void InsertCustomer(CustomerDataModel customerDataModel);
void UpdateCustomer(CustomerDataModel customerDataModel);
void DeleteCustomer(string id);
}

View File

@@ -0,0 +1,12 @@
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,15 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IPostBusinessLogicContract
{
List<PostDataModel> GetAllPosts();
List<PostDataModel> GetAllDataOfPost(string postId);
List<PostDataModel> GetPostHistory(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,18 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
/// <summary>
/// Интерфейс бизнес-логики
/// </summary>
internal interface IProductBusinessLogicContract
{
List<ProductDataModel> GetAllProducts(bool onlyActive = true);
ProductDataModel GetProductById(string id);
void InsertProduct(ProductDataModel saleProductDataModel);
void UpdateProduct(ProductDataModel saleProductDataModel);
void DeleteProduct(string id);
List<ProductIngredientDataModel> GetIngredientsByProductId(string saleProductId);
void AddIngredientToProduct(ProductIngredientDataModel ProductIngredient);
void RemoveIngredientFromProduct(string ingredientId, string productId);
}

View File

@@ -0,0 +1,25 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface IReportContract
{
Task<List<IngredientWithHistoryDataModel>> GetDataIngredientsWithHistoryAsync(CancellationToken ct); // Переименвоал
Task<List<SaleDataModel>> GetDataSaleByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<WorkerSalaryByPeriodDataModel>> GetDataSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentIngredientsWithHistoryAsync(CancellationToken ct); // Переименовал
Task<Stream> CreateDocumentSalesByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentSalaryByPeriodAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
// таски для харда
Task<List<SupplyInformationDataModel>> GetDataSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<IngredientMoveDataModel>> GetDataIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<StorageIngredientDataModel>> GetDataStorageStockAsync(string storageId, CancellationToken ct);
Task<Stream> CreateDocumentSupplyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentIngredientMovementAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentStorageStockAsync(string storageId, CancellationToken ct);
}

View File

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

View File

@@ -0,0 +1,15 @@
using CandyHouseContracts.DataModels;
namespace CandyHouseContracts.BusinessLogicsContracts;
internal interface ISaleBusinessLogicContract
{
List<SaleDataModel> GetAllSalesByPeriod(DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByWorkerByPeriod(string workerId, DateTime fromDate, DateTime toDate);
List<SaleDataModel> GetAllSalesByCustomerByPeriod(string customerId, 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);
double CalculateSalesRevenue(DateTime fromDate, DateTime toDate);
}

View File

@@ -0,0 +1,18 @@
using CandyHouseContracts.DataModels;
using System.Collections.Generic;
namespace CandyHouseContracts.BusinessLogicsContracts
{
internal interface IStorageBusinessLogicContract
{
List<StorageDataModel> GetAllStorages();
void CreateStorage(string id, string address, int count, List<StorageIngredientDataModel> ingredients);
void UpdateStorage(StorageDataModel storage);
void AddIngredientToStorage(string storageId, StorageIngredientDataModel ingredient);
void UpdateIngredientCount(string storageId, string ingredientId, int newCount);
int GetIngredientCountFromStorage(string storageId, string ingredientId);
List<StorageIngredientDataModel> GetIngredientsFromStorage(string storageId);
StorageDataModel GetStorageById(string storageId);
Dictionary<string, int> CalculateRemainingIngredients(string storageId);
}
}

View File

@@ -0,0 +1,18 @@
using CandyHouseContracts.DataModels;
using System.Collections.Generic;
namespace CandyHouseContracts.BusinessLogicsContracts
{
internal interface ISupplyBusinessLogicContract
{
List<SupplyDataModel> GetAllSupplies();
void CreateSupply(SupplyDataModel supply);
SupplyDataModel GetSupplyById(string supplyId);
List<SupplyDataModel> GetSuppliesByStorageId(string storageId);
void AddOrUpdateIngredientInSupply(SupplyIngredientDataModel ingredient);
void UpdateIngredientCountInSupply(string supplyId, string ingredientId, int newCount);
void RemoveIngredientFromSupply(string supplyId, string ingredientId);
int CalculateSuppliedIngredientsCount(DateTime fromDate, DateTime toDate);
}
}

View File

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

View File

@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<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.Configuration.Binder" Version="9.0.4" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Messages.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Messages.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Messages.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Messages.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="CandyHouseBusinessLogic" />
<InternalsVisibleTo Include="CandyHouseDatabase" />
<InternalsVisibleTo Include="CandyHouseWebApi" />
<InternalsVisibleTo Include="CandyHouseTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,40 @@
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using System.Text.RegularExpressions;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Resources;
using Microsoft.Extensions.Localization;
namespace CandyHouseContracts.DataModels;
internal class CustomerDataModel(string id, string fio, string phone, string email, double discountSize) : IValidation
{
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string Phone { get; private set; } = phone;
public string Email { get; private set; } = email;
public double DiscountSize { get; private set; } = discountSize;
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 (Phone.IsEmpty()) throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Phone"));
var phoneRegex = new Regex(@"^\+7\d{10}$");
if (!phoneRegex.IsMatch(Phone)) throw new ValidationException(localizer["ValidationExceptionMessageIncorrectPhone"]);
if (Email.IsEmpty()) throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Email"));
if (!Email.Contains("@")) throw new ValidationException(localizer["ValidationExceptionMessageIncorrectEmail"]);
}
}

View File

@@ -0,0 +1,57 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class IngredientDataModel(string id, string ingredientName, IngredientType ingredientType, string manufacturerId, double price, bool isDeleted) : IValidation
{
public string Id { get; private set; } = id;
public string IngredientName { get; private set; } = ingredientName;
public IngredientType IngredientType { get; private set; } = ingredientType;
public string ManufacturerId { get; private set; } = manufacturerId;
public double Price { get; private set; } = price;
public bool IsDeleted { get; private set; } = isDeleted;
private readonly ManufacturerDataModel? _manufacturer;
public string ManufacturerName => _manufacturer?.ManufacturerName ?? string.Empty;
public IngredientDataModel(string id, string ingredientName, IngredientType ingredientType, string manufacturerId, double price, bool isDeleted, ManufacturerDataModel manufacturer) : this(id, ingredientName, ingredientType, manufacturerId, price, isDeleted)
{
_manufacturer = manufacturer;
}
public IngredientDataModel(string id, string ingredientName, IngredientType ingredientType, string manufacturerId, double price) : this(id, ingredientName, ingredientType, manufacturerId, price, 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 (IngredientName.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientName"));
if (IngredientType == IngredientType.None)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientType"));
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,38 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class IngredientHistoryDataModel(string ingredientId, double oldPrice) : IValidation
{
private readonly IngredientDataModel? _ingredient;
public string IngredientId { get; private set; } = ingredientId;
public double OldPrice { get; private set; } = oldPrice; // олд спайс
public DateTime ChangeDate { get; private set; } = DateTime.UtcNow; // дата изменения цены
public string IngredientName => _ingredient?.IngredientName ?? string.Empty;
public IngredientHistoryDataModel(string ingredientId, double oldPrice, DateTime changeDate, IngredientDataModel ingredient) : this(ingredientId, oldPrice)
{
ChangeDate = changeDate;
_ingredient = ingredient;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (IngredientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientId"));
if (!IngredientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
if (OldPrice <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "OldPrice"));
}
}

View File

@@ -0,0 +1,11 @@
namespace CandyHouseContracts.DataModels;
public class IngredientMoveDataModel
{
public DateTime Date { get; set; }
public required string OperationType { get; set; } // "Приход" или "Уход"
public required string IngredientId { get; set; }
public required string IngredientName { get; set; }
public int Count { get; set; }
public string? StorageId { get; set; }
}

View File

@@ -0,0 +1,41 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace CandyHouseContracts.DataModels;
internal class IngredientWithHistoryDataModel : IValidation
{
public string IngredientName { get; set; }
public double CurrentPrice { get; set; }
public string ManufacturerName { get; set; }
public List<IngredientHistoryDataModel> History { get; set; }
public IngredientWithHistoryDataModel(string ingredientName, double currentPrice, string manufacturerName, List<IngredientHistoryDataModel> history)
{
IngredientName = ingredientName;
CurrentPrice = currentPrice;
ManufacturerName = manufacturerName;
History = history;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (string.IsNullOrEmpty(IngredientName))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientName"));
if (string.IsNullOrEmpty(ManufacturerName))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ManufacturerName"));
if (CurrentPrice <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "CurrentPrice"));
if (History == null)
throw new ValidationException("Field History is null");
}
}

View File

@@ -0,0 +1,32 @@
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(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,57 @@
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Infrastructure.PostConfigurations;
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class PostDataModel(string postid, string postName, PostType postType, PostConfiguration configuration) : IValidation
{
public string Id { get; private set; } = postid;
public string PostName { get; private set; } = postName;
public PostType PostType { get; private set; } = postType;
public PostConfiguration ConfigurationModel { get; private set; } = configuration;
public PostDataModel(string postId, string postName, PostType postType, string configurationJson) : this(postId, postName, postType, (PostConfiguration)null)
{
var obj = JToken.Parse(configurationJson);
if (obj is not null)
{
ConfigurationModel = obj.Value<string>("Type") switch
{
nameof(CashierPostConfiguration) => JsonConvert.DeserializeObject<CashierPostConfiguration>(configurationJson)!,
nameof(SupervisorPostConfiguration) => JsonConvert.DeserializeObject<SupervisorPostConfiguration>(configurationJson)!,
_ => JsonConvert.DeserializeObject<PostConfiguration>(configurationJson)!,
};
}
}
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 (ConfigurationModel is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "ConfigurationModel"));
if (ConfigurationModel!.Rate <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Rate"));
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ProductIngredientDataModel(string ingredientId, string productId, int count) : IValidation
{
public string IngredientId { get; private set; } = ingredientId;
public string ProductId { get; private set; } = productId;
public int Count { get; private set; } = count; //Например для плашек оперативки
public void Validate(IStringLocalizer<Messages> localizer)
{
Console.WriteLine($"Validating ProductIngredientDataModel: ProductId={ProductId}, IngredientId={IngredientId}, Count={Count}");
if (IngredientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientId"));
if (!IngredientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
if (ProductId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "ProductId"));
if (!ProductId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "ProductId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

View File

@@ -0,0 +1,42 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class ProductDataModel(string id, string productName, int count, double totalPrice, List<ProductIngredientDataModel> ingredients, bool isDeleted = false) : IValidation
{
public string Id { get; private set; } = id; // Id товара, т.к. в этой сущности хранится готовый к продаже ПК
public string ProductName { get; private set; } = productName; // Теперь тут название ПК, а не ProductID (ID выше добавил)
public int Count { get; private set; } = count;
public double TotalPrice { get; private set; } = totalPrice;
public List<ProductIngredientDataModel> Ingredients { get; private set; } = ingredients;
public bool IsDeleted { get; private set; } = isDeleted;
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 (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if (TotalPrice <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "TotalPrice"));
if (Ingredients == null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "Ingredients"));
if (Ingredients.Count == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoIngredientInProduct"]);
Ingredients.ForEach(x => x.Validate(localizer));
}
}

View File

@@ -0,0 +1,36 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SalaryDataModel(string workerId, DateTime salaryDate, double workerSalary) : IValidation
{
private readonly WorkerDataModel? _worker;
public string WorkerId { get; private set; } = workerId;
public DateTime SalaryDate { get; private set; } = salaryDate.ToUniversalTime();
public double Salary { get; private set; } = workerSalary;
public string WorkerFIO => _worker?.FIO ?? string.Empty;
public SalaryDataModel(string workerId, DateTime salaryDate, double workerSalary, WorkerDataModel worker) : this(workerId, salaryDate, workerSalary)
{
_worker = worker;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (WorkerId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkerId"));
if (!WorkerId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
if (Salary <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Salary"));
}
}

View File

@@ -0,0 +1,105 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Enums;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
using System.Xml;
namespace CandyHouseContracts.DataModels;
internal class SaleDataModel : IValidation
{
private readonly CustomerDataModel? _customer;
private readonly WorkerDataModel? _worker;
public string Id { get; private set; }
public string WorkerId { get; private set; }
public string? CustomerId { 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 ProductDataModel? Product { get; private set; }
public string CustomerFIO => _customer?.FIO ?? string.Empty;
public string WorkerFIO => _worker?.FIO ?? string.Empty;
public SaleDataModel(string id, string workerId, string? customerId, DiscountType discountType, bool isCancel, ProductDataModel product)
{
Id = id;
WorkerId = workerId;
CustomerId = customerId;
DiscountType = discountType;
IsCancel = isCancel;
Product = product;
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 и Discount
Sum = Product != null ? Product.TotalPrice * Product.Count : 0;
Discount = Sum * percent;
}
public SaleDataModel(string id, string workerId, string? customerId, double sum, DiscountType discountType, double discount, bool isCancel, ProductDataModel Product, WorkerDataModel worker, CustomerDataModel? customer) : this(id, workerId, customerId, discountType, isCancel, Product)
{
Sum = sum;
Discount = discount;
_worker = worker;
_customer = customer;
}
public SaleDataModel(string id, string workerId, string? customerId, int discountType, ProductDataModel product) : this(id, workerId, customerId, (DiscountType)discountType, false, product) { }
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 (WorkerId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "WorkerId"));
if (!WorkerId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "WorkerId"));
if (CustomerId != null && (!CustomerId.IsGuid() || CustomerId.IsEmpty()))
throw new ValidationException("The value in the field CustomerId is not a unique identifier");
if (Sum <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Sum"));
if (Product is null)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "Products"));
if (!Product.Id.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "Id"));
if (Product.Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class StorageIngredientDataModel(string storageId, string ingredientId, int count) : IValidation
{
public string StorageId { get; private set; } = storageId;
public string IngredientId { get; private set; } = ingredientId;
public int Count { get; private set; } = count;
public void Validate(IStringLocalizer<Messages> localizer)
{
if (StorageId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "StorageId"));
if (!StorageId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "StorageId"));
if (IngredientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientId"));
if (!IngredientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class StorageDataModel(string id, string address, int count, List<StorageIngredientDataModel> ingredients) : IValidation
{
public string Id { get; private set; } = id;
public string Address { get; private set; } = address;
public int Count { get; private set; } = count;
public List<StorageIngredientDataModel> Ingredients { get; private set; } = ingredients;
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 (Address.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "Address"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

View File

@@ -0,0 +1,32 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SupplyIngredientDataModel(string supplyId, string ingredientId, int count) : IValidation
{
public string SupplyId { get; private set; } = supplyId;
public string IngredientId { get; private set; } = ingredientId;
public int Count { get; private set; } = count;
public void Validate(IStringLocalizer<Messages> localizer)
{
if (SupplyId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SupplyId"));
if (!SupplyId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "SupplyId"));
if (IngredientId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientId"));
if (!IngredientId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "IngredientId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

View File

@@ -0,0 +1,29 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SupplyIngredientReportDataModel
{
public string SupplyId { get; set; }
public string IngredientId { get; set; }
public int Count { get; set; }
public SupplyIngredientReportDataModel(string supplyId, string ingredientId, int count)
{
SupplyId = supplyId;
IngredientId = ingredientId;
Count = count;
}
public void Validate(IStringLocalizer<Messages> localizer)
{
if (string.IsNullOrWhiteSpace(SupplyId))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SupplyId"));
if (string.IsNullOrWhiteSpace(IngredientId))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "IngredientId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
}
}

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 SupplyDataModel(string id, string storageId, DateTime deliveryDate, int count, List<SupplyIngredientDataModel> ingredients) : IValidation
{
public string Id { get; private set; } = id;
public string StorageId { get; private set; } = storageId;
public DateTime DeliveryDate { get; private set; } = deliveryDate.ToUniversalTime();
public int Count { get; private set; } = count;
public List<SupplyIngredientDataModel> Ingredients { get; private set; } = ingredients;
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 (StorageId.IsEmpty())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "StorageId"));
if (!StorageId.IsGuid())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotAId"], "StorageId"));
if (Count <= 0)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageLessOrEqualZero"], "Count"));
if ((Ingredients?.Count ?? 0) == 0)
throw new ValidationException(localizer["ValidationExceptionMessageNoIngredientInSupply"]);
if (DeliveryDate > DateTime.UtcNow)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageDeliveryDateInFuture"], DeliveryDate.ToShortDateString()));
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class SupplyInformationDataModel
{
public string SupplyId { get; set; }
public DateTime SupplyDate { get; set; }
public string StorageId { get; set; }
public List<SupplyIngredientReportDataModel> Ingredients { get; set; }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (string.IsNullOrWhiteSpace(SupplyId))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "SupplyId"));
if (SupplyDate == default)
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "SupplyDate"));
if (string.IsNullOrWhiteSpace(StorageId))
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageEmptyField"], "StorageId"));
if (Ingredients == null || !Ingredients.Any())
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageNotInitialized"], "Ingredients"));
foreach (var ingredient in Ingredients)
{
ingredient.Validate(localizer);
}
}
}

View File

@@ -0,0 +1,65 @@
using Microsoft.Extensions.Localization;
using CandyHouseContracts.Exceptions;
using CandyHouseContracts.Extensions;
using CandyHouseContracts.Infrastructure;
using CandyHouseContracts.Infrastructure.PostConfigurations;
using CandyHouseContracts.Resources;
namespace CandyHouseContracts.DataModels;
internal class WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted) : IValidation
{
private readonly PostDataModel? _post;
public string Id { get; private set; } = id;
public string FIO { get; private set; } = fio;
public string PostId { get; private set; } = postId;
public DateTime BirthDate { get; private set; } = birthDate.ToUniversalTime();
public DateTime EmploymentDate { get; private set; } = employmentDate.ToUniversalTime();
public bool IsDeleted { get; private set; } = isDeleted; // признак уволился или нет
public string PostName => _post?.PostName ?? string.Empty;
public PostConfiguration Configuration => _post?.ConfigurationModel ?? new() { Rate = 0 };
public WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate, bool isDeleted, PostDataModel post) : this(id, fio, postId, birthDate, employmentDate, isDeleted)
{
_post = post;
}
public WorkerDataModel(string id, string fio, string postId, DateTime birthDate, DateTime employmentDate) : this(id, fio, postId, birthDate, employmentDate, false) { }
public void Validate(IStringLocalizer<Messages> localizer)
{
if (Id.IsEmpty())
throw new ValidationException(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.UtcNow.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) // EmploymentDate.Year - BirthDate.Year
throw new ValidationException(string.Format(localizer["ValidationExceptionMessageMinorsEmploymentDate"],
EmploymentDate.ToShortDateString(), BirthDate.ToShortDateString()));
}
}

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