77 Commits

Author SHA1 Message Date
a690777272 Merge pull request 'Task_7.5_UI_Reports' (#12) from Task_7.5_UI_Reports into main
Reviewed-on: #12
2025-05-27 22:25:08 +04:00
9401c42ffd feat!: первая готовая версия веб интерфейса 2025-05-27 22:24:05 +04:00
6b1f57a3b9 feat: clerk ui 2025-05-27 21:55:13 +04:00
b8a9409dad feat: отчеты для клерка 2025-05-27 18:20:55 +04:00
2b24a8d6f2 fix: id для word и excel кладовщика 2025-05-27 01:40:12 +04:00
0a831f0a6c feat: cdn работа с pdf.js 2025-05-27 01:28:21 +04:00
bde930a544 feat: merge branches reports with prev ui version 2025-05-26 21:19:24 +04:00
b70143484d fix: добавление надписей методов в котроллере 2025-05-25 23:18:20 +04:00
75c0be18ce fix: добавление названий методов 2025-05-25 11:53:41 +04:00
a26193512c feat: добавление выборки для отчетов 2025-05-24 22:49:10 +04:00
6c561fb147 Merge pull request 'Task_7.5_UI_Reports' (#11) from Task_7.5_UI_Reports into main
Reviewed-on: #11
Breakin Change
2025-05-22 12:31:01 +04:00
46a8843a84 feat: кладовщик
Breaking Change
2025-05-22 12:30:04 +04:00
ce492dd8a1 feat: готовый excel 2025-05-22 11:17:16 +04:00
1153b716f4 feat: ебать 2025-05-22 03:59:48 +04:00
b6d3d53856 Merge branch 'Task_7_UI' into Task_7.5_UI_Reports 2025-05-21 23:20:21 +04:00
9138a12e97 fix: убрал word и doc просмотр у кладовщика 2025-05-21 23:03:41 +04:00
ed2369ed85 fix: небольшие фиксы 2025-05-21 22:13:22 +04:00
92d02d4ba6 feat: простенький экран отчетов 2025-05-21 21:16:49 +04:00
b977e76302 Merge branch 'main' into Task_7_UI 2025-05-21 19:24:24 +04:00
4fdc420920 Merge pull request 'Task забыл' (#10) from Task_5_Api into main
Reviewed-on: #10
2025-05-21 19:22:58 +04:00
50e870f28c Merge pull request 'Task_6_Report' (#8) from Task_6_Report into Task_5_Api
Reviewed-on: #8
feat: генерация и отправка отчетов на почту
2025-05-21 19:21:51 +04:00
4068a579c8 feat: готовые отчеты, отправка на почту 2025-05-21 18:23:34 +04:00
8e930475a3 feat: clerk ui, частично не рабочий из-за маппингов 2025-05-21 17:58:54 +04:00
57f878a051 feat: начало вьюхи клерка, готовы три таба, ни один не работает нормально, спасибо автомапперу 2025-05-21 17:43:16 +04:00
58ff192d7c fix: fix 2025-05-21 10:40:01 +04:00
ecff1045b3 feat: отправка почты 2025-05-21 09:52:51 +04:00
b59bdf9f3d feat: подготовка ui для клерка 2025-05-21 01:51:45 +04:00
94f602b0fe fix: правки (заработал первый отчет) 2025-05-20 23:28:39 +04:00
23a5de4e3e fix: изменение нэйминга, добавление тестов для отчетов 2025-05-20 22:45:18 +04:00
f5d5ff4b24 merge 2025-05-20 22:31:55 +04:00
109639617e feat: догика отчетов, начало тестирования 2025-05-20 17:33:09 +04:00
3e48ad4d24 Merge pull request 'Task_5_Api' (#7) from Task_5_Api into main
Reviewed-on: #7
feat: реализация web api, которая скорее всего работает, но чиниться будет уже в следующих тасках
2025-05-20 13:49:59 +04:00
b1e5b7de93 fix: поправил ошибки на формочке кредитных программ 2025-05-20 00:31:38 +04:00
9ed33690cf feat: первая простенькая версия ui для кладовщика 2025-05-20 00:00:45 +04:00
50b507fef3 feat: добавление адаптера, контроллера для отчетов, добавление логики в стораджах, добавление моделей для очетов, добвление бизнес-логики отчетов (неполная:(() 2025-05-19 18:53:24 +04:00
1c0bf1efd2 feat: немного поменял маппинг, сделал отображение нормальное без View модели с фио, для кладовщика основные формы и создание готово, осталось редактирования 2025-05-19 02:21:13 +04:00
e8f493691f feat: авторизация для кладовщика 2025-05-19 00:13:37 +04:00
abeeedaa64 feat: добавление логики(не полной) для создания отчетов и отправки на почту 2025-05-18 14:45:16 +04:00
a201d60ff3 feat: подготовка для отчетов 2025-05-18 12:57:05 +04:00
244b846ef9 feat: добавление некоторых контрактов для отчетов 2025-05-18 12:27:13 +04:00
de879be266 fix: добавил создание кредитных программ, починил по тупому cors и id на клиенте решил генерить 2025-05-18 02:52:11 +04:00
164def1e18 feat: popup формочка для добавления, хуки для загрузки данных 2025-05-18 02:20:03 +04:00
56053a7287 feat: ui для header и footer 2025-05-17 13:21:33 +04:00
24833faba0 feat: два класса тестов + базовый. 2025-05-10 13:13:38 +04:00
1cbde02887 fix: удаление юзинга 2025-05-10 13:12:28 +04:00
f4ad6ba98e feat: модели, адаптеры, контроллеры для кладовщика, валюты, пополнения 2025-05-03 16:26:07 +04:00
fbba161390 fix: изменение названия метода 2025-05-03 15:18:21 +04:00
d827ade763 feat: модели, адаптеры, контроллеры для клиента, вклада, кредитной программы. модели для связей многие ко многим 2025-05-02 14:45:07 +04:00
d6ded9f5e0 fix: убрал не нужные юзинги 2025-05-02 12:36:22 +04:00
bb1432fa4d feat: апи для сроков 2025-05-02 00:56:52 +04:00
5b25dcf976 fix: фикс дат в бизнес логике сроков и названий в контрактах клерка 2025-05-02 00:56:35 +04:00
6273bff7fb feat: добавление swagger документации 2025-05-01 18:06:01 +04:00
fa9dbb3f60 feat!: создание проекта web api
создание проекта для web api, конфигурации бд и тп, была ошибка с чтением конфигурации из json, решилась
2025-05-01 17:25:49 +04:00
1854214aa9 fix: тест Try_GetElementById_WhenHaveRecord_Test
в тесте проходило, что список пустой и это не одно и то же, если он null, для нашей реализации это одно и то же
2025-05-01 15:57:00 +04:00
3552e461fb Merge pull request 'Task_4_Storage' (#5) from Task_4_Storage into main
Reviewed-on: #5
2025-05-01 15:39:29 +04:00
43ae6247d5 Merge pull request 'Bank_Tests' (#6) from Bank_Tests into Task_4_Storage
Reviewed-on: #6
Первая версия тестов для контрактов бд, ещё не все мапперы настроены, будем фиксить в процессе работы
2025-05-01 15:26:49 +04:00
f270d36d22 feat: тесты, начало проверки многие ко многим 2025-05-01 15:24:28 +04:00
d636fb821e feat: тесты для всез классов, проверяющие получение списка, добавление и обновление, но не проверяющие многие ко многим 2025-04-30 12:35:29 +04:00
8d90b79a44 feat: новые тесты
Добавлены тесты для клерка, кредитной программы, валюты, вклада
2025-04-29 15:34:46 +04:00
46bfedef8d feat: создание проекта тестов
добавление двух с половиной классов тестов, пока не проверяет многие ко многим
2025-04-28 13:10:31 +04:00
78a053d6c0 feat: старт проекта тестов 2025-04-26 17:08:45 +04:00
514eb9ed19 fix: фикс гет по айди, который писал на https://git.is.ulstu.ru/slavaxom9k/PIBD-23_Coursework_Bank/pulls/5/files#issuecomment-56085 2025-04-26 13:17:34 +04:00
853d483ea5 feat: первая версия контрактов для хранения по кладовщику 2025-04-26 12:10:12 +04:00
3ae7c03f1c feat: сторадж контракты клерка 2025-04-26 11:21:39 +04:00
707f74dac9 feat: зависимости многие ко многим 2025-04-26 10:41:21 +04:00
a3e013f5ec fix: измение моделей 2025-04-26 09:59:20 +04:00
bcb1820207 fix: изменение проверок, убраны списки 2025-04-26 09:27:32 +04:00
d3797f12ab feat: подготовка моделей к для связи многие ко многи 2025-04-25 09:57:08 +04:00
9ed15b9d5e feat: реализация контракта хранилища кладовщика 2025-04-25 09:56:12 +04:00
0923f8dcf7 feat: добавил поля в моделях и модели многие ко многим 2025-04-25 09:54:23 +04:00
b582d03d45 fix: добавил списки в дата модель клерка 2025-04-25 01:13:35 +04:00
e909fa709f feat: создание проекта бд
добавление моделек для бд (кроме многие ко многим), реализация одного контракта хранилища
2025-04-25 01:12:51 +04:00
80d0ed46e6 Merge pull request 'Третий Task, реализация бизнес логики' (#4) from Task_3_BusinessLogic into main
Reviewed-on: #4
2025-04-24 18:45:52 +04:00
3e00bde5d0 fix: вот теперь точно поправил сообщения логгера и немного отформатировал 2025-04-24 16:32:17 +04:00
1b55bc7bf5 fix: поправил комменты и сообщения у логгера 2025-04-24 16:29:47 +04:00
a658f23eb6 feat: создание проекта бизнес логики, реализация бизнес логики для всех сущностей, имеющих контракт на реализацию этой логики, изменения в интерфесах контрактов хранилищ, для синхронизации их с бизнес логикой 2025-04-24 16:18:59 +04:00
6bf47bb02d Merge pull request 'Вторая таска - Интерфейсы' (#3) from Task_2_BusinessLogic into main
Reviewed-on: #3
Reviewed-by: chillya <chillya@noreply.git.student.athene.tech>
прикол что он localhost 3000)
2025-04-20 12:28:14 +04:00
347 changed files with 29508 additions and 21 deletions

View File

@@ -6,4 +6,20 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.3.0" />
<PackageReference Include="MailKit" Version="4.12.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
<PackageReference Include="PdfSharp.MigraDoc.Standard" Version="1.51.15" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BankContracts\BankContracts.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="BankWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,70 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для клерка
/// </summary>
/// <param name="clerkStorageContract">контракт хранилища</param>
/// <param name="logger">логгер</param>
internal class ClerkBusinessLogicContract(
IClerkStorageContract clerkStorageContract,
ILogger logger
) : IClerkBusinessLogicContract
{
private readonly IClerkStorageContract _clerkStorageContract = clerkStorageContract;
private readonly ILogger _logger = logger;
public List<ClerkDataModel> GetAllClerks()
{
_logger.LogInformation("get all clerks");
return _clerkStorageContract.GetList();
}
public ClerkDataModel GetClerkByData(string data)
{
_logger.LogInformation($"Get clerk by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _clerkStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
if (Regex.IsMatch(data, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
{
return _clerkStorageContract.GetElementByPhoneNumber(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
return _clerkStorageContract.GetElementByLogin(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertClerk(ClerkDataModel clerkDataModel)
{
_logger.LogInformation(
"Insert storekeeper: {storekeeper}",
JsonSerializer.Serialize(clerkDataModel)
);
ArgumentNullException.ThrowIfNull(clerkDataModel);
clerkDataModel.Validate();
_clerkStorageContract.AddElement(clerkDataModel);
}
public void UpdateClerk(ClerkDataModel clerkDataModel)
{
_logger.LogInformation("Update clerk: {clerk}", JsonSerializer.Serialize(clerkDataModel));
ArgumentNullException.ThrowIfNull(clerkDataModel);
clerkDataModel.Validate();
_clerkStorageContract.UpdElement(clerkDataModel);
}
}

View File

@@ -0,0 +1,86 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для клиента
/// </summary>
/// <param name="clientStorageContract">контракт клиента</param>
/// <param name="clerkStorageContract">контракт клерка</param>
/// <param name="logger">логгер</param>
internal class ClientBusinessLogicContract(
IClientStorageContract clientStorageContract,
IClerkStorageContract clerkStorageContract,
ILogger logger
) : IClientBusinessLogicContract
{
private readonly IClientStorageContract _clientStorageContract = clientStorageContract;
private readonly IClerkStorageContract _clerkStorageContract = clerkStorageContract;
private readonly ILogger _logger = logger;
public List<ClientDataModel> GetAllClients()
{
_logger.LogInformation("get all clients");
return _clientStorageContract.GetList();
}
public List<ClientDataModel> GetClientByClerk(string clerkId)
{
_logger.LogInformation("GetClientByClerk params: {clerkId}", clerkId);
if (clerkId.IsEmpty())
{
throw new ArgumentNullException(nameof(clerkId));
}
if (!clerkId.IsGuid())
{
throw new ValidationException(
"The value in the field clerkId is not a unique identifier."
);
}
return _clientStorageContract.GetList(clerkId: clerkId)
?? throw new NullListException($"{clerkId}");
}
public ClientDataModel GetClientByData(string data)
{
_logger.LogInformation("Get client by data: {data}", data);
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _clientStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertClient(ClientDataModel clientDataModel)
{
_logger.LogInformation(
"Insert client: {client}",
JsonSerializer.Serialize(clientDataModel)
);
ArgumentNullException.ThrowIfNull(clientDataModel);
clientDataModel.Validate();
_clientStorageContract.AddElement(clientDataModel);
}
public void UpdateClient(ClientDataModel clientDataModel)
{
_logger.LogInformation(
"Update client: {client}",
JsonSerializer.Serialize(clientDataModel)
);
ArgumentNullException.ThrowIfNull(clientDataModel);
clientDataModel.Validate();
_clientStorageContract.UpdElement(clientDataModel);
}
}

View File

@@ -0,0 +1,105 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для кредитной программы
/// </summary>
/// <param name="cpStorageContract">контракт хранилища кредитной программы</param>
/// <param name="logger">логгер</param>
internal class CreditProgramBusinessLogicContract(
ICreditProgramStorageContract cpStorageContract,
ILogger logger
) : ICreditProgramBusinessLogicContract
{
private readonly ICreditProgramStorageContract _creditProgramStorageContract =
cpStorageContract;
private readonly ILogger _logger = logger;
public List<CreditProgramDataModel> GetAllCreditPrograms()
{
_logger.LogInformation("get all credit programs");
return _creditProgramStorageContract.GetList();
}
public CreditProgramDataModel GetCreditProgramByData(string data)
{
_logger.LogInformation($"Get creadit program by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _creditProgramStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
throw new ElementNotFoundException($"element not found: {data}");
}
public List<CreditProgramDataModel> GetCreditProgramByPeriod(string periodId)
{
_logger.LogInformation("GetCreditProgramByPeriod params: {periodId}", periodId);
if (periodId.IsEmpty())
{
throw new ArgumentNullException(nameof(periodId));
}
if (!periodId.IsGuid())
{
throw new ValidationException(
"The value in the field periodId is not a unique identifier."
);
}
return _creditProgramStorageContract.GetList(periodId: periodId)
?? throw new NullListException($"{periodId}");
}
public List<CreditProgramDataModel> GetCreditProgramByStorekeeper(string storekeeperId)
{
_logger.LogInformation(
"GetCreditProgramByStorekeeper params: {storekeeperId}",
storekeeperId
);
if (storekeeperId.IsEmpty())
{
throw new ArgumentNullException(nameof(storekeeperId));
}
if (!storekeeperId.IsGuid())
{
throw new ValidationException(
"The value in the field clerkId is not a unique identifier."
);
}
return _creditProgramStorageContract.GetList(storekeeperId: storekeeperId)
?? throw new NullListException($"{storekeeperId}");
}
public void InsertCreditProgram(CreditProgramDataModel creditProgramDataModel)
{
_logger.LogInformation(
"Insert credit program: {credit program}",
JsonSerializer.Serialize(creditProgramDataModel)
);
ArgumentNullException.ThrowIfNull(creditProgramDataModel);
creditProgramDataModel.Validate();
_creditProgramStorageContract.AddElement(creditProgramDataModel);
}
public void UpdateCreditProgram(CreditProgramDataModel creditProgramDataModel)
{
_logger.LogInformation(
"Update credit program: {credit program}",
JsonSerializer.Serialize(creditProgramDataModel)
);
ArgumentNullException.ThrowIfNull(creditProgramDataModel);
creditProgramDataModel.Validate();
_creditProgramStorageContract.UpdElement(creditProgramDataModel);
}
}

View File

@@ -0,0 +1,85 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для валюты
/// </summary>
/// <param name="currencyStorageContract">контракт валюты</param>
/// <param name="logger">логгер</param>
internal class CurrencyBusinessLogicContract(
ICurrencyStorageContract currencyStorageContract,
ILogger logger
) : ICurrencyBusinessLogicContract
{
private readonly ICurrencyStorageContract _currencyStorageContract = currencyStorageContract;
private readonly ILogger _logger = logger;
public List<CurrencyDataModel> GetAllCurrencies()
{
_logger.LogInformation("get all currencys programs");
return _currencyStorageContract.GetList();
}
public CurrencyDataModel GetCurrencyByData(string data)
{
_logger.LogInformation($"Get currencys program by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _currencyStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
return _currencyStorageContract.GetElementByAbbreviation(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
public List<CurrencyDataModel> GetCurrencyByStorekeeper(string storekeeperId)
{
_logger.LogInformation("GetCurrencyByStorekeeper params: {storekeeperId}", storekeeperId);
if (storekeeperId.IsEmpty())
{
throw new ArgumentNullException(nameof(storekeeperId));
}
if (!storekeeperId.IsGuid())
{
throw new ValidationException(
"The value in the field storekeeperId is not a unique identifier."
);
}
return _currencyStorageContract.GetList(storekeeperId: storekeeperId)
?? throw new NullListException($"{storekeeperId}");
}
public void InsertCurrency(CurrencyDataModel currencyDataModel)
{
_logger.LogInformation(
"Insert currency: {currency}",
JsonSerializer.Serialize(currencyDataModel)
);
ArgumentNullException.ThrowIfNull(currencyDataModel);
currencyDataModel.Validate();
_currencyStorageContract.AddElement(currencyDataModel);
}
public void UpdateCurrency(CurrencyDataModel currencyDataModel)
{
_logger.LogInformation(
"Update currency: {currency}",
JsonSerializer.Serialize(currencyDataModel)
);
ArgumentNullException.ThrowIfNull(currencyDataModel);
currencyDataModel.Validate();
_currencyStorageContract.UpdElement(currencyDataModel);
}
}

View File

@@ -0,0 +1,88 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
using static System.Single;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для вклада
/// </summary>
/// <param name="depositStorageContract">контракт вклада</param>
/// <param name="logger">логгер</param>
internal class DepositBusinessLogicContract(
IDepositStorageContract depositStorageContract,
ILogger logger
) : IDepositBusinessLogicContract
{
private readonly IDepositStorageContract _depositStorageContract = depositStorageContract;
private readonly ILogger _logger = logger;
public List<DepositDataModel> GetAllDeposits()
{
_logger.LogInformation("get all deposits");
return _depositStorageContract.GetList();
}
public List<DepositDataModel> GetDepositByClerk(string clerkId)
{
_logger.LogInformation("GetDepositByClerk params: {clerkId}", clerkId);
if (clerkId.IsEmpty())
{
throw new ArgumentNullException(nameof(clerkId));
}
if (!clerkId.IsGuid())
{
throw new ValidationException(
"The value in the field clerkId is not a unique identifier."
);
}
return _depositStorageContract.GetList(clerkId: clerkId)
?? throw new NullListException($"{clerkId}");
}
public DepositDataModel GetDepositByData(string data)
{
_logger.LogInformation($"GetDepositByData by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _depositStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
return TryParse(data, out var result) // пофиксить!!!
? _depositStorageContract.GetElementByInterestRate(result) ??
throw new ElementNotFoundException($"element not found: {data}")
: throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertDeposit(DepositDataModel depositDataModel)
{
_logger.LogInformation(
"Insert credit program: {credit program}",
JsonSerializer.Serialize(depositDataModel)
);
ArgumentNullException.ThrowIfNull(depositDataModel);
depositDataModel.Validate();
_depositStorageContract.AddElement(depositDataModel);
}
public void UpdateDeposit(DepositDataModel depositDataModel)
{
_logger.LogInformation(
"Update credit program: {credit program}",
JsonSerializer.Serialize(depositDataModel)
);
ArgumentNullException.ThrowIfNull(depositDataModel);
depositDataModel.Validate();
_depositStorageContract.UpdElement(depositDataModel);
}
}

View File

@@ -0,0 +1,69 @@
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using System;
using System.Threading.Tasks;
namespace BankBusinessLogic.Implementations
{
public class EmailService
{
private readonly string _smtpServer;
private readonly int _smtpPort;
private readonly string _smtpUsername;
private readonly string _smtpPassword;
public EmailService(string smtpServer, int smtpPort, string smtpUsername, string smtpPassword)
{
_smtpServer = smtpServer;
_smtpPort = smtpPort;
_smtpUsername = smtpUsername;
_smtpPassword = smtpPassword;
}
public static EmailService CreateYandexService()
{
return new EmailService(
smtpServer: "smtp.yandex.ru",
smtpPort: 465,
smtpUsername: "egoffevgeny@yandex.com",
smtpPassword: "mpaffjmvyulsdpev"
);
}
public async Task SendReportAsync(string toEmail, string subject, string body, string attachmentPath = null)
{
var email = new MimeMessage();
email.From.Add(new MailboxAddress("Bank System", _smtpUsername));
email.To.Add(new MailboxAddress("", toEmail));
email.Subject = subject;
var builder = new BodyBuilder();
builder.HtmlBody = body;
if (!string.IsNullOrEmpty(attachmentPath))
{
builder.Attachments.Add(attachmentPath);
}
email.Body = builder.ToMessageBody();
using (var smtp = new SmtpClient())
{
await smtp.ConnectAsync(_smtpServer, _smtpPort, SecureSocketOptions.SslOnConnect);
await smtp.AuthenticateAsync(_smtpUsername, _smtpPassword);
await smtp.SendAsync(email);
await smtp.DisconnectAsync(true);
}
}
public async Task SendTestEmailAsync(string toEmail)
{
await SendReportAsync(
toEmail: toEmail,
subject: "Тестовое письмо от банковской системы",
body: "<h1>Тестовое письмо</h1><p>Это тестовое письмо отправлено для проверки работы системы.</p>"
);
}
}
}

View File

@@ -0,0 +1,93 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для сроков
/// </summary>
/// <param name="periodStorageContract">контракт сроков</param>
/// <param name="logger">логгер</param>
internal class PeriodBusinessLogicContract(
IPeriodStorageContract periodStorageContract,
ILogger logger
) : IPeriodBusinessLogicContract
{
private readonly IPeriodStorageContract _periodStorageContract = periodStorageContract;
private readonly ILogger _logger = logger;
public List<PeriodDataModel> GetAllPeriods()
{
_logger.LogInformation("get all periods");
return _periodStorageContract.GetList();
}
public List<PeriodDataModel> GetAllPeriodsByStartTime(DateTime fromDate)
{
if (fromDate.IsDateNotOlder(DateTime.UtcNow))
{
throw new IncorrectDatesException(fromDate, DateTime.UtcNow);
}
return _periodStorageContract.GetList(startDate: fromDate).OrderBy(x => x.StartTime).ToList()
?? throw new NullListException(nameof(PeriodDataModel));
}
public List<PeriodDataModel> GetAllPeriodsByEndTime(DateTime toDate)
{
return _periodStorageContract.GetList(endDate: toDate).OrderBy(x => x.EndTime).ToList()
?? throw new NullListException(nameof(PeriodDataModel));
}
public List<PeriodDataModel> GetAllPeriodsByStorekeeper(string storekeeperId)
{
_logger.LogInformation("GetAllPeriodsByStorekeeper params: {storekeeperId}", storekeeperId);
if (storekeeperId.IsEmpty())
{
throw new ArgumentNullException(nameof(storekeeperId));
}
if (!storekeeperId.IsGuid())
{
throw new ValidationException(
"The value in the field storekeeperId is not a unique identifier."
);
}
return _periodStorageContract.GetList(storekeeperId: storekeeperId)
?? throw new NullListException($"{storekeeperId}");
}
public PeriodDataModel GetPeriodByData(string data)
{
_logger.LogInformation($"GetPeriodByData by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _periodStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertPeriod(PeriodDataModel periodataModel)
{
_logger.LogInformation("Insert period: {period}", JsonSerializer.Serialize(periodataModel));
ArgumentNullException.ThrowIfNull(periodataModel);
periodataModel.Validate();
_periodStorageContract.AddElement(periodataModel);
}
public void UpdatePeriod(PeriodDataModel periodataModel)
{
_logger.LogInformation("Update period: {period}", JsonSerializer.Serialize(periodataModel));
ArgumentNullException.ThrowIfNull(periodataModel);
periodataModel.Validate();
_periodStorageContract.UpdElement(periodataModel);
}
}

View File

@@ -0,0 +1,115 @@
using System.Text.Json;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для пополнения
/// </summary>
/// <param name="replenishmentStorageContract">контракт пополнения</param>
/// <param name="logger">логгер</param>
internal class ReplenishmentBusinessLogicContract(
IReplenishmentStorageContract replenishmentStorageContract,
ILogger logger
) : IReplenishmentBusinessLogicContract
{
private readonly IReplenishmentStorageContract _replenishmentStorageContract =
replenishmentStorageContract;
private readonly ILogger _logger = logger;
public List<ReplenishmentDataModel> GetAllReplenishments()
{
_logger.LogInformation("get all replenishments");
return _replenishmentStorageContract.GetList();
}
public List<ReplenishmentDataModel> GetAllReplenishmentsByClerk(string clerkId)
{
_logger.LogInformation("GetAllReplenishmentsByClerk params: {clerkId}", clerkId);
if (clerkId.IsEmpty())
{
throw new ArgumentNullException(nameof(clerkId));
}
if (!clerkId.IsGuid())
{
throw new ValidationException(
"The value in the field clerkId is not a unique identifier."
);
}
return _replenishmentStorageContract.GetList(clerkId: clerkId)
?? throw new NullListException($"{clerkId}");
}
public List<ReplenishmentDataModel> GetAllReplenishmentsByDate(
DateTime fromDate,
DateTime toDate
)
{
if (toDate.IsDateNotOlder(fromDate))
{
throw new IncorrectDatesException(fromDate, toDate);
}
return _replenishmentStorageContract.GetList(fromDate, toDate).ToList()
?? throw new NullListException(nameof(PeriodDataModel));
}
public List<ReplenishmentDataModel> GetAllReplenishmentsByDeposit(string depositId)
{
_logger.LogInformation("GetAllReplenishmentsByClerk params: {depositId}", depositId);
if (depositId.IsEmpty())
{
throw new ArgumentNullException(nameof(depositId));
}
if (!depositId.IsGuid())
{
throw new ValidationException(
"The value in the field depositId is not a unique identifier."
);
}
return _replenishmentStorageContract.GetList(depositId: depositId)
?? throw new NullListException($"{depositId}");
}
public ReplenishmentDataModel GetReplenishmentByData(string data)
{
_logger.LogInformation($"GetReplenishmentByData by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _replenishmentStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertReplenishment(ReplenishmentDataModel replenishmentataModel)
{
_logger.LogInformation(
"Insert replenishment: {replenishment}",
JsonSerializer.Serialize(replenishmentataModel)
);
ArgumentNullException.ThrowIfNull(replenishmentataModel);
replenishmentataModel.Validate();
_replenishmentStorageContract.AddElement(replenishmentataModel);
}
public void UpdateReplenishment(ReplenishmentDataModel replenishmentataModel)
{
_logger.LogInformation(
"Update replenishment: {replenishment}",
JsonSerializer.Serialize(replenishmentataModel)
);
ArgumentNullException.ThrowIfNull(replenishmentataModel);
replenishmentataModel.Validate();
_replenishmentStorageContract.UpdElement(replenishmentataModel);
}
}

View File

@@ -0,0 +1,454 @@
using BankBusinessLogic.OfficePackage;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
public class ReportContract(IClientStorageContract clientStorage, ICurrencyStorageContract currencyStorage,
ICreditProgramStorageContract creditProgramStorage, IDepositStorageContract depositStorage,
BaseWordBuilder baseWordBuilder, BaseExcelBuilder baseExcelBuilder, BasePdfBuilder basePdfBuilder, ILogger logger) : IReportContract
{
private readonly IClientStorageContract _clientStorage = clientStorage;
private readonly ICurrencyStorageContract _currencyStorage = currencyStorage;
private readonly ICreditProgramStorageContract _creditProgramStorage = creditProgramStorage;
private readonly IDepositStorageContract _depositStorage = depositStorage;
private readonly BaseWordBuilder _baseWordBuilder = baseWordBuilder;
private readonly BaseExcelBuilder _baseExcelBuilder = baseExcelBuilder;
private readonly BasePdfBuilder _basePdfBuilder = basePdfBuilder;
private readonly ILogger _logger = logger;
private static readonly string[] documentHeader = ["Название программы", "Фамилия", "Имя", "Баланс"];
private static readonly string[] depositHeader = ["Название программы", "Ставка", "Сумма", "Срок"];
private static readonly string[] clientsByDepositHeader = ["Фамилия", "Имя", "Баланс", "Ставка", "Срок", "Период"];
private static readonly string[] currencyHeader = ["Валюта", "Кредитная программа", "Макс. сумма", "Ставка", "Срок"];
/// <summary>
/// Получения данных для отчета Клиента по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
public async Task<List<ClientsByCreditProgramDataModel>> GetDataClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Get data ClientsByCreditProgram");
if (creditProgramIds is null || creditProgramIds.Count == 0)
{
return [];
}
var clients = await Task.Run(() => _clientStorage.GetList(), ct);
var creditPrograms = await Task.Run(() => _creditProgramStorage.GetList(), ct);
var currencies = await Task.Run(() => _currencyStorage.GetList(), ct);
var filteredPrograms = creditPrograms
.Where(cp => creditProgramIds.Contains(cp.Id));
return filteredPrograms
.Select(cp => new ClientsByCreditProgramDataModel
{
CreditProgramId = cp.Id,
CreditProgramName = cp.Name,
ClientSurname = clients.Where(c => c.CreditProgramClients.Any(cpc => cpc.CreditProgramId == cp.Id))
.Select(c => c.Surname).ToList(),
ClientName = clients.Where(c => c.CreditProgramClients.Any(cpc => cpc.CreditProgramId == cp.Id))
.Select(c => c.Name).ToList(),
ClientBalance = clients.Where(c => c.CreditProgramClients.Any(cpc => cpc.CreditProgramId == cp.Id))
.Select(c => c.Balance).ToList()
}).ToList();
}
/// <summary>
/// Создание word отчета Клиента по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Create report ClientsByCreditProgram");
var data = await GetDataClientsByCreditProgramAsync(creditProgramIds, ct) ?? throw new InvalidOperationException("No found data");
var tableRows = new List<string[]>
{
documentHeader
};
foreach (var program in data)
{
for (int i = 0; i < program.ClientSurname.Count; i++)
{
tableRows.Add(
[
program.CreditProgramName,
program.ClientSurname[i],
program.ClientName[i],
program.ClientBalance[i].ToString("N2")
]);
}
}
return _baseWordBuilder
.AddHeader("Клиенты по кредитным программам")
.AddParagraph($"Сформировано на дату {DateTime.Now}")
.AddTable([2000, 2000, 2000, 2000], tableRows)
.Build();
}
/// <summary>
/// Создание excel отчета Клиенты по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateExcelDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Create Excel report ClientsByCreditProgram");
var data = await GetDataClientsByCreditProgramAsync(creditProgramIds, ct) ?? throw new InvalidOperationException("No found data");
var tableRows = new List<string[]>
{
documentHeader
};
foreach (var program in data)
{
for (int i = 0; i < program.ClientSurname.Count; i++)
{
tableRows.Add(
[
program.CreditProgramName,
program.ClientSurname[i],
program.ClientName[i],
program.ClientBalance[i].ToString("N2")
]);
}
}
return _baseExcelBuilder
.AddHeader("Клиенты по кредитным программам", 0, 4)
.AddParagraph($"Сформировано на дату {DateTime.Now}", 0)
.AddTable([25, 25, 25, 25], tableRows)
.Build();
}
/// <summary>
/// Получение данных для отчета Клиента по Депозитам
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateFinish"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public async Task<List<ClientsByDepositDataModel>> GetDataClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data ClientsByDeposit from {dateStart} to {dateFinish}", dateStart, dateFinish);
if (dateStart > dateFinish)
{
throw new ArgumentException("Start date cannot be later than finish date");
}
var clients = await Task.Run(() => _clientStorage.GetList(), ct);
var deposits = await Task.Run(() => _depositStorage.GetList(), ct);
var result = new List<ClientsByDepositDataModel>();
foreach (var client in clients)
{
if (client.DepositClients == null || !client.DepositClients.Any())
{
continue;
}
foreach (var depositClient in client.DepositClients)
{
var deposit = deposits.FirstOrDefault(d => d.Id == depositClient.DepositId);
if (deposit == null)
{
continue;
}
result.Add(new ClientsByDepositDataModel
{
ClientSurname = client.Surname,
ClientName = client.Name,
ClientBalance = client.Balance,
DepositRate = deposit.InterestRate,
DepositPeriod = deposit.Period,
FromPeriod = dateStart,
ToPeriod = dateFinish
});
}
}
//if (!result.Any())
//{
// throw new InvalidOperationException("No clients with deposits found");
//}
return result;
}
/// <summary>
/// Создание pdf отчета Клиента по Депозитам
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateFinish"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateDocumentClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report ClientsByDeposit from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataClientsByDepositAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
// Двухуровневый заголовок
var tableRows = new List<string[]>
{
new string[] { "Клиент", "Клиент", "Клиент", "Вклад", "Вклад", "Период" },
new string[] { "Фамилия", "Имя", "Баланс", "Ставка", "Срок", "" }
};
foreach (var client in data)
{
tableRows.Add(
[
client.ClientSurname,
client.ClientName,
client.ClientBalance.ToString("N2"),
client.DepositRate.ToString("N2"),
$"{client.DepositPeriod} мес.",
$"{client.FromPeriod.ToShortDateString()} - {client.ToPeriod.ToShortDateString()}"
]);
}
return _basePdfBuilder
.AddHeader("Клиенты по вкладам")
.AddParagraph($"за период с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}")
.AddTable([25, 25, 25, 25, 25, 25], tableRows)
.Build();
}
/// <summary>
/// Получение данных для отчета Депозиты и Кредитные программы по Валютам
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateFinish"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public async Task<List<CreditProgramAndDepositByCurrencyDataModel>> GetDataDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Get data DepositAndCreditProgramByCurrency from {dateStart} to {dateFinish}", dateStart, dateFinish);
if (dateStart > dateFinish)
{
throw new ArgumentException("Start date cannot be later than finish date");
}
var currencies = await Task.Run(() => _currencyStorage.GetList(), ct);
var creditPrograms = await Task.Run(() => _creditProgramStorage.GetList(), ct);
var deposits = await Task.Run(() => _depositStorage.GetList(), ct);
return currencies.Select(c => new CreditProgramAndDepositByCurrencyDataModel
{
CurrencyName = c.Name,
CreditProgramName = creditPrograms.Where(cp => cp.Currencies.Any(cc => cc.CurrencyId == c.Id))
.Select(cp => cp.Name).ToList(),
CreditProgramMaxCost = creditPrograms.Where(cp => cp.Currencies.Any(cc => cc.CurrencyId == c.Id))
.Select(cp => (int)cp.MaxCost).ToList(),
DepositRate = deposits.Where(d => d.Currencies.Any(dc => dc.CurrencyId == c.Id))
.Select(d => d.InterestRate).ToList(),
DepositPeriod = deposits.Where(d => d.Currencies.Any(dc => dc.CurrencyId == c.Id))
.Select(d => d.Period).ToList(),
FromPeriod = dateStart,
ToPeriod = dateFinish
}).ToList();
}
/// <summary>
/// Создание pdf отчета Депозиты и Кредитные программы по Валютам
/// </summary>
/// <param name="dateStart"></param>
/// <param name="dateFinish"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateDocumentDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct)
{
_logger.LogInformation("Create report DepositAndCreditProgramByCurrency from {dateStart} to {dateFinish}", dateStart, dateFinish);
var data = await GetDataDepositAndCreditProgramByCurrencyAsync(dateStart, dateFinish, ct) ?? throw new InvalidOperationException("No found data");
// Двухуровневый заголовок
var tableRows = new List<string[]>
{
new string[] { "Наименование валюты", "Кредитная программа", "Кредитная программа", "Вклад", "Вклад" },
new string[] { "", "Название", "Максимальная сумма", "Процентная ставка", "Срок" }
};
foreach (var currency in data)
{
// Вывод информации по кредитным программам
for (int i = 0; i < currency.CreditProgramName.Count; i++)
{
// Вычисляем индекс депозита, если есть соответствующие
string depositRate = "—";
string depositPeriod = "—";
// Проверяем, есть ли депозиты для этой валюты и не вышли ли мы за границы массива
if (currency.DepositRate.Count > 0)
{
// Берем индекс по модулю, чтобы не выйти за границы массива
int depositIndex = i % currency.DepositRate.Count;
depositRate = currency.DepositRate[depositIndex].ToString("N2");
depositPeriod = $"{currency.DepositPeriod[depositIndex]} мес.";
}
// Добавляем строку в таблицу
tableRows.Add(
[
currency.CurrencyName,
currency.CreditProgramName[i],
currency.CreditProgramMaxCost[i].ToString("N2"),
depositRate,
depositPeriod
]);
}
// Если есть депозиты, но нет кредитных программ, добавляем строки только с депозитами
if (currency.CreditProgramName.Count == 0 && currency.DepositRate.Count > 0)
{
for (int j = 0; j < currency.DepositRate.Count; j++)
{
tableRows.Add(
[
currency.CurrencyName,
"—",
"—",
currency.DepositRate[j].ToString("N2"),
$"{currency.DepositPeriod[j]} мес."
]);
}
}
}
return _basePdfBuilder
.AddHeader("Вклады и кредитные программы по валютам")
.AddParagraph($"за период с {dateStart.ToShortDateString()} по {dateFinish.ToShortDateString()}")
.AddTable([25, 30, 25, 25, 25], tableRows)
.Build();
}
/// <summary>
/// Получение данных для отчета Депозиты по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<List<DepositByCreditProgramDataModel>> GetDataDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Get data DepositByCreditProgram");
var deposits = await Task.Run(() => _depositStorage.GetList(), ct);
var creditPrograms = await Task.Run(() => _creditProgramStorage.GetList(), ct);
// Проверяем, что у вкладов есть связанные валюты
if (!deposits.Any(d => d.Currencies.Any()))
{
throw new InvalidOperationException("No deposits with currencies found");
}
var filteredPrograms = creditPrograms
.Where(cp => creditProgramIds == null || creditProgramIds.Contains(cp.Id));
return filteredPrograms.Select(cp => new DepositByCreditProgramDataModel
{
CreditProgramName = cp.Name,
DepositRate = deposits.Select(d => d.InterestRate).ToList(),
DepositCost = deposits.Select(d => d.Cost).ToList(),
DepositPeriod = deposits.Select(d => d.Period).ToList()
}).ToList();
}
/// <summary>
/// Создание word отчета Депозиты по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Create report DepositByCreditProgram");
var data = await GetDataDepositByCreditProgramAsync(creditProgramIds, ct) ?? throw new InvalidOperationException("No found data");
var tableRows = new List<string[]>
{
depositHeader
};
foreach (var program in data)
{
for (int i = 0; i < program.DepositRate.Count; i++)
{
tableRows.Add(
[
program.CreditProgramName,
program.DepositRate[i].ToString("N2"),
program.DepositCost[i].ToString("N2"),
program.DepositPeriod[i].ToString()
]);
}
}
return _baseWordBuilder
.AddHeader("Вклады по кредитным программам")
.AddParagraph($"Сформировано на дату {DateTime.Now}")
.AddTable([2000, 2000, 2000, 2000], tableRows)
.Build();
}
/// <summary>
/// Создание excel отчета Депозиты по Кредитным программам
/// </summary>
/// <param name="creditProgramIds"></param>
/// <param name="ct"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<Stream> CreateExcelDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct)
{
_logger.LogInformation("Create Excel report DepositByCreditProgram");
var data = await GetDataDepositByCreditProgramAsync(creditProgramIds, ct) ?? throw new InvalidOperationException("No found data");
var tableRows = new List<string[]>
{
depositHeader
};
foreach (var program in data)
{
for (int i = 0; i < program.DepositRate.Count; i++)
{
tableRows.Add(
[
program.CreditProgramName,
program.DepositRate[i].ToString("N2"),
program.DepositCost[i].ToString("N2"),
program.DepositPeriod[i].ToString()
]);
}
}
return _baseExcelBuilder
.AddHeader("Вклады по кредитным программам", 0, 4)
.AddParagraph($"Сформировано на дату {DateTime.Now}", 0)
.AddTable([25, 25, 25, 25], tableRows)
.Build();
}
}

View File

@@ -0,0 +1,75 @@
using System.Text.Json;
using System.Text.RegularExpressions;
using BankContracts.BusinessLogicContracts;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.StorageContracts;
using Microsoft.Extensions.Logging;
namespace BankBusinessLogic.Implementations;
/// <summary>
/// реализация бизнес логики для кладовщика
/// </summary>
/// <param name="storekeeperStorageContract">контракт кладовщика</param>
/// <param name="logger">логгер</param>
internal class StorekeeperBusinessLogicContract(
IStorekeeperStorageContract storekeeperStorageContract,
ILogger logger
) : IStorekeeperBusinessLogicContract
{
private readonly IStorekeeperStorageContract _storekeeperStorageContract =
storekeeperStorageContract;
private readonly ILogger _logger = logger;
public List<StorekeeperDataModel> GetAllStorekeepers()
{
_logger.LogInformation("get all storekeepers");
return _storekeeperStorageContract.GetList();
}
public StorekeeperDataModel GetStorekeeperByData(string data)
{
_logger.LogInformation($"Get storekeeper by data: {data}");
if (data.IsEmpty())
{
throw new ArgumentNullException(nameof(data));
}
if (data.IsGuid())
{
return _storekeeperStorageContract.GetElementById(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
if (Regex.IsMatch(data, @"^((8|\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$"))
{
return _storekeeperStorageContract.GetElementByPhoneNumber(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
return _storekeeperStorageContract.GetElementByLogin(data)
?? throw new ElementNotFoundException($"element not found: {data}");
}
public void InsertStorekeeper(StorekeeperDataModel storekeeperDataModel)
{
_logger.LogInformation(
"Insert storekeeper: {storekeeper}",
JsonSerializer.Serialize(storekeeperDataModel)
);
ArgumentNullException.ThrowIfNull(storekeeperDataModel);
storekeeperDataModel.Validate();
_storekeeperStorageContract.AddElement(storekeeperDataModel);
}
public void UpdateStorekeeper(StorekeeperDataModel storekeeperDataModel)
{
_logger.LogInformation(
"Update storekeeper: {storekeeper}",
JsonSerializer.Serialize(storekeeperDataModel)
);
ArgumentNullException.ThrowIfNull(storekeeperDataModel);
storekeeperDataModel.Validate();
_storekeeperStorageContract.UpdElement(storekeeperDataModel);
}
}

View File

@@ -0,0 +1,9 @@
namespace BankBusinessLogic.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,9 @@
namespace BankBusinessLogic.OfficePackage;
public abstract class BasePdfBuilder
{
public abstract BasePdfBuilder AddHeader(string header);
public abstract BasePdfBuilder AddParagraph(string text);
public abstract BasePdfBuilder AddTable(int[] columnsWidths, List<string[]> data);
public abstract Stream Build();
}

View File

@@ -0,0 +1,9 @@
namespace BankBusinessLogic.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,155 @@
using MigraDoc.DocumentObjectModel;
using MigraDoc.DocumentObjectModel.Tables;
using MigraDoc.Rendering;
using System.Text;
namespace BankBusinessLogic.OfficePackage;
public class MigraDocPdfBuilder : BasePdfBuilder
{
private readonly Document _document;
public MigraDocPdfBuilder()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
_document = new Document();
_document.Info.Title = "Bank Report";
_document.DefaultPageSetup.LeftMargin = Unit.FromCentimeter(2);
_document.DefaultPageSetup.RightMargin = Unit.FromCentimeter(2);
_document.DefaultPageSetup.TopMargin = Unit.FromCentimeter(2);
_document.DefaultPageSetup.BottomMargin = Unit.FromCentimeter(2);
DefineStyles();
}
public override BasePdfBuilder AddHeader(string header)
{
var section = _document.AddSection();
var paragraph = section.AddParagraph(header, "Heading1");
paragraph.Format.SpaceAfter = Unit.FromPoint(10);
return this;
}
public override BasePdfBuilder AddParagraph(string text)
{
var section = _document.LastSection ?? _document.AddSection();
var paragraph = section.AddParagraph(text, "Normal");
paragraph.Format.SpaceAfter = Unit.FromPoint(10);
return this;
}
public override BasePdfBuilder 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");
var section = _document.LastSection ?? _document.AddSection();
var table = section.AddTable();
table.Style = "Table";
table.Borders.Width = 0.75;
table.Borders.Color = Colors.Black;
table.Rows.LeftIndent = 0;
// Добавляем столбцы с заданной шириной
foreach (var width in columnsWidths)
{
var widthInCm = width / 10.35;
var column = table.AddColumn(Unit.FromCentimeter(widthInCm));
column.Format.Alignment = ParagraphAlignment.Left;
}
// Первая строка — объединённый заголовок
var headerRow1 = table.AddRow();
headerRow1.HeadingFormat = true;
headerRow1.Format.Font.Bold = true;
headerRow1.Format.Alignment = ParagraphAlignment.Center;
headerRow1.VerticalAlignment = VerticalAlignment.Center;
headerRow1.Shading.Color = Colors.White;
for (int j = 0; j < data[0].Length; j++)
{
var cell = headerRow1.Cells[j];
cell.AddParagraph(data[0][j]);
cell.Format.Alignment = ParagraphAlignment.Center;
cell.VerticalAlignment = VerticalAlignment.Center;
cell.Borders.Width = 0.5;
}
// Объединяем ячейки: "Кредитная программа" (j=1,2), "Вклад" (j=3,4)
headerRow1.Cells[1].MergeRight = 1;
headerRow1.Cells[3].MergeRight = 1;
// Вторая строка — подзаголовки
var headerRow2 = table.AddRow();
headerRow2.HeadingFormat = true;
headerRow2.Format.Font.Bold = true;
headerRow2.Format.Alignment = ParagraphAlignment.Center;
headerRow2.VerticalAlignment = VerticalAlignment.Center;
headerRow2.Shading.Color = Colors.White;
for (int j = 0; j < data[1].Length; j++)
{
var cell = headerRow2.Cells[j];
cell.AddParagraph(data[1][j]);
cell.Format.Alignment = ParagraphAlignment.Center;
cell.VerticalAlignment = VerticalAlignment.Center;
cell.Borders.Width = 0.5;
}
// Данные — обычные строки, без жирности и заливки, выравнивание по левому краю
for (int i = 2; i < data.Count; i++)
{
var row = table.AddRow();
row.Format.Font.Bold = false;
row.Format.Alignment = ParagraphAlignment.Left;
row.VerticalAlignment = VerticalAlignment.Center;
row.Shading.Color = Colors.White;
for (int j = 0; j < data[i].Length; j++)
{
var cell = row.Cells[j];
cell.AddParagraph(data[i][j]);
cell.Format.Alignment = ParagraphAlignment.Left;
cell.VerticalAlignment = VerticalAlignment.Center;
cell.Borders.Width = 0.5;
}
}
section.AddParagraph();
return this;
}
public override Stream Build()
{
var stream = new MemoryStream();
var renderer = new PdfDocumentRenderer(true)
{
Document = _document
};
renderer.RenderDocument();
renderer.PdfDocument.Save(stream);
stream.Position = 0; // Важно установить позицию в начало потока
return stream;
}
private void DefineStyles()
{
// Определяем стиль для обычного текста
var style = _document.Styles["Normal"];
style.Font.Name = "Times New Roman";
style.Font.Size = 12;
style.ParagraphFormat.SpaceAfter = Unit.FromPoint(10);
// Определяем стиль для заголовка
style = _document.Styles.AddStyle("Heading1", "Normal");
style.Font.Bold = true;
style.Font.Size = 14;
style.ParagraphFormat.SpaceAfter = Unit.FromPoint(10);
style.ParagraphFormat.Alignment = ParagraphAlignment.Center;
// Определяем стиль для таблицы
style = _document.Styles.AddStyle("Table", "Normal");
style.Font.Name = "Times New Roman";
style.Font.Size = 10;
style.ParagraphFormat.SpaceAfter = Unit.FromPoint(5);
}
}

View File

@@ -0,0 +1,297 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
namespace BankBusinessLogic.OfficePackage;
public 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 = 1;
_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; ++i)
{
for (var j = 0; j < data[i].Length; ++j)
{
CreateCell(j, _rowIndex, data[i][j], StyleIndex.SimpleTextWithBorder);
}
_rowIndex++;
}
return this;
}
public override Stream Build()
{
var stream = new MemoryStream();
using var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook);
var workbookpart = spreadsheetDocument.AddWorkbookPart();
GenerateStyle(workbookpart);
workbookpart.Workbook = new Workbook();
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
if (_columns.HasChildren)
{
worksheetPart.Worksheet.Append(_columns);
}
worksheetPart.Worksheet.Append(_sheetData);
var sheets = spreadsheetDocument.WorkbookPart!.Workbook.AppendChild(new Sheets());
var sheet = new Sheet()
{
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Лист 1"
};
sheets.Append(sheet);
if (_mergeCells.HasChildren)
{
worksheetPart.Worksheet.InsertAfter(_mergeCells, worksheetPart.Worksheet.Elements<SheetData>().First());
}
return stream;
}
private static void GenerateStyle(WorkbookPart workbookPart)
{
var workbookStylesPart = workbookPart.AddNewPart<WorkbookStylesPart>();
workbookStylesPart.Stylesheet = new Stylesheet();
var fonts = new Fonts() { Count = 2, KnownFonts = BooleanValue.FromBoolean(true) };
fonts.Append(new DocumentFormat.OpenXml.Spreadsheet.Font
{
FontSize = new FontSize() { Val = 11 },
FontName = new FontName() { Val = "Calibri" },
FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
FontScheme = new FontScheme() { Val = new EnumValue<FontSchemeValues>(FontSchemeValues.Minor) }
});
fonts.Append(new DocumentFormat.OpenXml.Spreadsheet.Font
{
FontSize = new FontSize() { Val = 11 },
FontName = new FontName() { Val = "Calibri" },
FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
FontScheme = new FontScheme() { Val = new EnumValue<FontSchemeValues>(FontSchemeValues.Minor) },
Bold = new Bold()
});
workbookStylesPart.Stylesheet.Append(fonts);
// Default Fill
var fills = new Fills() { Count = 1 };
fills.Append(new Fill
{
PatternFill = new PatternFill() { PatternType = new EnumValue<PatternValues>(PatternValues.None) }
});
workbookStylesPart.Stylesheet.Append(fills);
// Default Border
var borders = new Borders() { Count = 2 };
borders.Append(new Border
{
LeftBorder = new LeftBorder(),
RightBorder = new RightBorder(),
TopBorder = new TopBorder(),
BottomBorder = new BottomBorder(),
DiagonalBorder = new DiagonalBorder()
});
borders.Append(new Border
{
LeftBorder = new LeftBorder() { Style = BorderStyleValues.Thin },
RightBorder = new RightBorder() { Style = BorderStyleValues.Thin },
TopBorder = new TopBorder() { Style = BorderStyleValues.Thin },
BottomBorder = new BottomBorder() { Style = BorderStyleValues.Thin }
});
workbookStylesPart.Stylesheet.Append(borders);
// Default cell format and a date cell format
var cellFormats = new CellFormats() { Count = 4 };
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 0,
BorderId = 0,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Left,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 0,
BorderId = 1,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Left,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 1,
BorderId = 0,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Center,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
cellFormats.Append(new CellFormat
{
NumberFormatId = 0,
FormatId = 0,
FontId = 1,
BorderId = 1,
FillId = 0,
Alignment = new Alignment()
{
Horizontal = HorizontalAlignmentValues.Center,
Vertical = VerticalAlignmentValues.Center,
WrapText = true
}
});
workbookStylesPart.Stylesheet.Append(cellFormats);
}
private enum StyleIndex
{
SimpleTextWithoutBorder = 0,
SimpleTextWithBorder = 1,
BoldTextWithoutBorder = 2,
BoldTextWithBorder = 3
}
private void CreateCell(int columnIndex, uint rowIndex, string text, StyleIndex styleIndex)
{
var columnName = GetExcelColumnName(columnIndex);
var cellReference = columnName + rowIndex;
var row = _sheetData.Elements<Row>().FirstOrDefault(r => r.RowIndex! == rowIndex);
if (row == null)
{
row = new Row() { RowIndex = rowIndex };
_sheetData.Append(row);
}
var newCell = row.Elements<Cell>()
.FirstOrDefault(c => c.CellReference != null && c.CellReference.Value == columnName + rowIndex);
if (newCell == null)
{
Cell? refCell = null;
foreach (Cell cell in row.Elements<Cell>())
{
if (cell.CellReference?.Value != null && cell.CellReference.Value.Length == cellReference.Length)
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
}
newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
}
newCell.CellValue = new CellValue(text);
newCell.DataType = CellValues.String;
newCell.StyleIndex = (uint)styleIndex;
}
private static string GetExcelColumnName(int columnNumber)
{
columnNumber += 1;
int dividend = columnNumber;
string columnName = string.Empty;
int modulo;
while (dividend > 0)
{
modulo = (dividend - 1) % 26;
columnName = Convert.ToChar(65 + modulo).ToString() + columnName;
dividend = (dividend - modulo) / 26;
}
return columnName;
}
}

View File

@@ -0,0 +1,94 @@
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
namespace BankBusinessLogic.OfficePackage;
public 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,20 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
/// <summary>
/// контракт адаптера для клерка
/// </summary>
public interface IClerkAdapter
{
ClerkOperationResponse GetList();
ClerkOperationResponse GetElement(string data);
ClerkOperationResponse RegisterClerk(ClerkBindingModel clerkModel);
ClerkOperationResponse ChangeClerkInfo(ClerkBindingModel clerkModel);
ClerkOperationResponse Login(LoginBindingModel clerkModel, out string token);
}

View File

@@ -0,0 +1,25 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BankContracts.AdapterContracts;
/// <summary>
/// контракт адаптера для клиента
/// </summary>
public interface IClientAdapter
{
ClientOperationResponse GetList();
ClientOperationResponse GetElement(string data);
ClientOperationResponse GetListByClerk(string clerkId);
ClientOperationResponse RegisterClient(ClientBindingModel clientModel);
ClientOperationResponse ChangeClientInfo(ClientBindingModel clientModel);
}

View File

@@ -0,0 +1,19 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
public interface ICreditProgramAdapter
{
CreditProgramOperationResponse GetList();
CreditProgramOperationResponse GetElement(string data);
CreditProgramOperationResponse GetListByStorekeeper(string storekeeperId);
CreditProgramOperationResponse GetListByPeriod(string periodId);
CreditProgramOperationResponse RegisterCreditProgram(CreditProgramBindingModel creditProgramModel);
CreditProgramOperationResponse ChangeCreditProgramInfo(CreditProgramBindingModel creditProgramModel);
}

View File

@@ -0,0 +1,20 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
/// <summary>
/// контракт адаптера для валюыты
/// </summary>
public interface ICurrencyAdapter
{
CurrencyOperationResponse GetList();
CurrencyOperationResponse GetElement(string data);
CurrencyOperationResponse GetListByStorekeeper(string storekeeperId);
CurrencyOperationResponse MakeCurrency(CurrencyBindingModel currencyModel);
CurrencyOperationResponse ChangeCurrencyInfo(CurrencyBindingModel currencyModel);
}

View File

@@ -0,0 +1,20 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
/// <summary>
/// контракт адаптера для вклада
/// </summary>
public interface IDepositAdapter
{
DepositOperationResponse GetList();
DepositOperationResponse GetElement(string data);
DepositOperationResponse GetListByClerk(string clerkId);
DepositOperationResponse MakeDeposit(DepositBindingModel depositModel);
DepositOperationResponse ChangeDepositInfo(DepositBindingModel depositModel);
}

View File

@@ -0,0 +1,21 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
public interface IPeriodAdapter
{
PeriodOperationResponse GetList();
PeriodOperationResponse GetElement(string data);
PeriodOperationResponse GetListByStorekeeper(string storekeeperId);
PeriodOperationResponse GetListByStartTime(DateTime fromDate);
PeriodOperationResponse GetListByEndTime(DateTime toDate);
PeriodOperationResponse RegisterPeriod(PeriodBindingModel periodModel);
PeriodOperationResponse ChangePeriodInfo(PeriodBindingModel periodModel);
}

View File

@@ -0,0 +1,21 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
public interface IReplenishmentAdapter
{
ReplenishmentOperationResponse GetList();
ReplenishmentOperationResponse GetElement(string data);
ReplenishmentOperationResponse GetListByClerk(string clerkId);
ReplenishmentOperationResponse GetListByDeposit(string depositId);
ReplenishmentOperationResponse GetListByDate(DateTime fromDate, DateTime toDate);
ReplenishmentOperationResponse RegisterReplenishment(ReplenishmentBindingModel replenishmentModel);
ReplenishmentOperationResponse ChangeReplenishmentInfo(ReplenishmentBindingModel replenishmentModel);
}

View File

@@ -0,0 +1,18 @@
using BankContracts.AdapterContracts.OperationResponses;
namespace BankContracts.AdapterContracts;
public interface IReportAdapter
{
Task<ReportOperationResponse> GetDataClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> GetDataClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> GetDataDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> GetDataDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> CreateExcelDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> CreateExcelDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<ReportOperationResponse> CreateDocumentDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -0,0 +1,20 @@
using BankContracts.AdapterContracts.OperationResponses;
using BankContracts.BindingModels;
namespace BankContracts.AdapterContracts;
/// <summary>
/// контракт адаптера для кладовщика
/// </summary>
public interface IStorekeeperAdapter
{
StorekeeperOperationResponse GetList();
StorekeeperOperationResponse GetElement(string data);
StorekeeperOperationResponse RegisterStorekeeper(StorekeeperBindingModel storekeeperModel);
StorekeeperOperationResponse ChangeStorekeeperInfo(StorekeeperBindingModel storekeeperModel);
StorekeeperOperationResponse Login(LoginBindingModel storekeeperModel, out string token);
}

View File

@@ -0,0 +1,27 @@
using BankContracts.Infrastructure;
using BankContracts.ViewModels;
namespace BankContracts.AdapterContracts.OperationResponses;
public class ClerkOperationResponse : OperationResponse
{
public static ClerkOperationResponse OK(List<ClerkViewModel> data) =>
OK<ClerkOperationResponse, List<ClerkViewModel>>(data);
public static ClerkOperationResponse OK(ClerkViewModel data) =>
OK<ClerkOperationResponse, ClerkViewModel>(data);
public static ClerkOperationResponse NoContent() => NoContent<ClerkOperationResponse>();
public static ClerkOperationResponse NotFound(string message) =>
NotFound<ClerkOperationResponse>(message);
public static ClerkOperationResponse BadRequest(string message) =>
BadRequest<ClerkOperationResponse>(message);
public static ClerkOperationResponse InternalServerError(string message) =>
InternalServerError<ClerkOperationResponse>(message);
public static ClerkOperationResponse Unauthorized(string message) =>
Unauthorized<ClerkOperationResponse>(message);
}

View File

@@ -0,0 +1,29 @@
using BankContracts.Infrastructure;
using BankContracts.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BankContracts.AdapterContracts.OperationResponses;
public class ClientOperationResponse : OperationResponse
{
public static ClientOperationResponse OK(List<ClientViewModel> data) =>
OK<ClientOperationResponse, List<ClientViewModel>>(data);
public static ClientOperationResponse OK(ClientViewModel data) =>
OK<ClientOperationResponse, ClientViewModel>(data);
public static ClientOperationResponse NoContent() => NoContent<ClientOperationResponse>();
public static ClientOperationResponse NotFound(string message) =>
NotFound<ClientOperationResponse>(message);
public static ClientOperationResponse BadRequest(string message) =>
BadRequest<ClientOperationResponse>(message);
public static ClientOperationResponse InternalServerError(string message) =>
InternalServerError<ClientOperationResponse>(message);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
using BankContracts.Infrastructure;
using BankContracts.ViewModels;
namespace BankContracts.AdapterContracts.OperationResponses;
public class ReportOperationResponse : OperationResponse
{
public static ReportOperationResponse OK(List<ClientsByCreditProgramViewModel> data) => OK<ReportOperationResponse, List<ClientsByCreditProgramViewModel>>(data);
public static ReportOperationResponse OK(List<ClientsByDepositViewModel> data) => OK<ReportOperationResponse, List<ClientsByDepositViewModel>>(data);
public static ReportOperationResponse OK(List<DepositByCreditProgramViewModel> data) => OK<ReportOperationResponse, List<DepositByCreditProgramViewModel>>(data);
public static ReportOperationResponse OK(List<CreditProgramAndDepositByCurrencyViewModel> data) => OK<ReportOperationResponse, List<CreditProgramAndDepositByCurrencyViewModel>>(data);
public static ReportOperationResponse OK(Stream data, string fileName) => OK<ReportOperationResponse, Stream>(data, fileName);
public static ReportOperationResponse BadRequest(string message) => BadRequest<ReportOperationResponse>(message);
public static ReportOperationResponse InternalServerError(string message) => InternalServerError<ReportOperationResponse>(message);
public Stream? GetStream() => Result as Stream;
public string? GetFileName() => FileName;
}

View File

@@ -0,0 +1,30 @@
using BankContracts.Infrastructure;
using BankContracts.ViewModels;
namespace BankContracts.AdapterContracts.OperationResponses;
public class StorekeeperOperationResponse : OperationResponse
{
public static StorekeeperOperationResponse OK(List<StorekeeperViewModel> data) =>
OK<StorekeeperOperationResponse, List<StorekeeperViewModel>>(data);
public static StorekeeperOperationResponse OK(string token) =>
OK<StorekeeperOperationResponse, string>(token);
public static StorekeeperOperationResponse OK(StorekeeperViewModel data) =>
OK<StorekeeperOperationResponse, StorekeeperViewModel>(data);
public static StorekeeperOperationResponse NoContent() => NoContent<StorekeeperOperationResponse>();
public static StorekeeperOperationResponse NotFound(string message) =>
NotFound<StorekeeperOperationResponse>(message);
public static StorekeeperOperationResponse BadRequest(string message) =>
BadRequest<StorekeeperOperationResponse>(message);
public static StorekeeperOperationResponse InternalServerError(string message) =>
InternalServerError<StorekeeperOperationResponse>(message);
public static StorekeeperOperationResponse Unauthorized(string message) =>
Unauthorized<StorekeeperOperationResponse>(message);
}

View File

@@ -6,4 +6,10 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,23 @@
namespace BankContracts.BindingModels;
/// <summary>
/// модель ответа от клиента для клерка
/// </summary>
public class ClerkBindingModel
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? Surname { get; set; }
public string? MiddleName { get; set; }
public string? Login { get; set; }
public string? Password { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace BankContracts.BindingModels;
public class ClientBindingModel
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? Surname { get; set; }
public decimal Balance { get; set; }
public string? ClerkId { get; set; }
public List<DepositClientBindingModel>? DepositClients { get; set; }
public List<ClientCreditProgramBindingModel>? CreditProgramClients { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class ClientCreditProgramBindingModel
{
public string? CreditProgramId { get; set; }
public string? ClientId { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace BankContracts.BindingModels;
public class CreditProgramBindingModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public decimal Cost { get; set; }
public decimal MaxCost { get; set; }
public required string StorekeeperId { get; set; }
public required string PeriodId { get; set; }
public List<CreditProgramCurrencyBindingModel>? CurrencyCreditPrograms { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class CreditProgramCurrencyBindingModel
{
public string? CreditProgramId { get; set; }
public string? CurrencyId { get; set; }
}

View File

@@ -0,0 +1,16 @@
using System.Xml.Linq;
namespace BankContracts.BindingModels;
public class CurrencyBindingModel
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? Abbreviation { get; set; }
public decimal Cost { get; set; }
public string? StorekeeperId { get; set; }
}

View File

@@ -0,0 +1,17 @@
namespace BankContracts.BindingModels;
public class DepositBindingModel
{
public string? Id { get; set; }
public float InterestRate { get; set; }
public decimal Cost { get; set; }
public int Period { get; set; }
public string? ClerkId { get; set; }
public List<DepositCurrencyBindingModel>? DepositCurrencies { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class DepositClientBindingModel
{
public string? DepositId { get; set; }
public string? ClientId { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class DepositCurrencyBindingModel
{
public string? DepositId { get; set; }
public string? CurrencyId { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.BindingModels;
public class LoginBindingModel
{
public required string Login { get; set; }
public required string Password { get; set; }
}

View File

@@ -0,0 +1,16 @@
namespace BankContracts.BindingModels;
public class MailConfigBindingModel
{
public string MailLogin { get; set; } = string.Empty;
public string MailPassword { get; set; } = string.Empty;
public string SmtpClientHost { get; set; } = string.Empty;
public int SmtpClientPort { get; set; }
public string PopHost { get; set; } = string.Empty;
public int PopPort { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace BankContracts.BindingModels;
public class MailSendInfoBindingModel
{
public string ToEmail { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
public string? AttachmentPath { get; set; }
}

View File

@@ -0,0 +1,15 @@
namespace BankContracts.BindingModels;
/// <summary>
/// модель ответа от клиента для срока
/// </summary>
public class PeriodBindingModel
{
public string? Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string? StorekeeperId { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace BankContracts.BindingModels;
public class ReplenishmentBindingModel
{
public string? Id { get; set; }
public decimal Amount { get; set; }
public DateTime Date { get; set; }
public string? DepositId { get; set; }
public string? ClerkId { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace BankContracts.BindingModels;
public class ReportMailSendInfoBindingModel
{
public string Email { get; set; } = string.Empty;
public string Subject { get; set; } = string.Empty;
public string Body { get; set; } = string.Empty;
}
public class CreditProgramReportMailSendInfoBindingModel : ReportMailSendInfoBindingModel
{
public List<string> CreditProgramIds { get; set; } = new();
}
public class DepositReportMailSendInfoBindingModel : ReportMailSendInfoBindingModel
{
// Для отчетов по депозитам дополнительные поля передаются через query параметры
}

View File

@@ -0,0 +1,20 @@
namespace BankContracts.BindingModels;
public class StorekeeperBindingModel
{
public string? Id { get; set; }
public string? Name { get; set; }
public string? Surname { get; set; }
public string? MiddleName { get; set; }
public string? Login { get; set; }
public string? Password { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
}

View File

@@ -3,7 +3,7 @@ namespace BankContracts.BusinessLogicContracts;
public interface ICurrencyBusinessLogicContract
{
List<CurrencyDataModel> GetAllCurrencys();
List<CurrencyDataModel> GetAllCurrencies();
List<CurrencyDataModel> GetCurrencyByStorekeeper(string storekeeperId);

View File

@@ -10,9 +10,9 @@ public interface IPeriodBusinessLogicContract
List<PeriodDataModel> GetAllPeriodsByStorekeeper(string storekeeperId);
List<PeriodDataModel> GetAllPeriodsByStartTime(DateTime fromDate, DateTime toDate);
List<PeriodDataModel> GetAllPeriodsByStartTime(DateTime fromDate);
List<PeriodDataModel> GetAllPeriodsByEndTime(DateTime fromDate, DateTime toDate);
List<PeriodDataModel> GetAllPeriodsByEndTime(DateTime toDate);
void InsertPeriod(PeriodDataModel periodataModel);

View File

@@ -0,0 +1,20 @@
using BankContracts.DataModels;
namespace BankContracts.BusinessLogicContracts;
public interface IReportContract
{
Task<List<ClientsByCreditProgramDataModel>> GetDataClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<Stream> CreateDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<Stream> CreateExcelDocumentClientsByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<List<ClientsByDepositDataModel>> GetDataClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentClientsByDepositAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<List<DepositByCreditProgramDataModel>> GetDataDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<Stream> CreateDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<Stream> CreateExcelDocumentDepositByCreditProgramAsync(List<string>? creditProgramIds, CancellationToken ct);
Task<List<CreditProgramAndDepositByCurrencyDataModel>> GetDataDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
Task<Stream> CreateDocumentDepositAndCreditProgramByCurrencyAsync(DateTime dateStart, DateTime dateFinish, CancellationToken ct);
}

View File

@@ -1,7 +1,7 @@
using BankContracts.Exceptions;
using System.Text.RegularExpressions;
using BankContracts.Exceptions;
using BankContracts.Extensions;
using BankContracts.Infrastructure;
using System.Text.RegularExpressions;
namespace BankContracts.DataModels;
@@ -16,7 +16,16 @@ namespace BankContracts.DataModels;
/// <param name="password">пароль</param>
/// <param name="email">адрес электронной почты</param>
/// <param name="phoneNumber">номер телефона</param>
public class ClerkDataModel(string id, string name, string surname, string middleName, string login, string password, string email, string phoneNumber) : IValidation
public class ClerkDataModel(
string id,
string name,
string surname,
string middleName,
string login,
string password,
string email,
string phoneNumber
) : IValidation
{
public string Id { get; private set; } = id;
@@ -26,7 +35,7 @@ public class ClerkDataModel(string id, string name, string surname, string middl
public string MiddleName { get; private set; } = middleName;
public string Login { get; private set; } = login;
public string Login { get; private set; } = login;
public string Password { get; private set; } = password;

View File

@@ -12,7 +12,10 @@ namespace BankContracts.DataModels;
/// <param name="surname">фамилия клиента</param>
/// <param name="balance">баланс клиента</param>
/// <param name="clerkId">уникальный Guid индентификатор клерка</param>
public class ClientDataModel(string id, string name, string surname, decimal balance, string clerkId) : IValidation
/// <param name="depositClients">вклады клиента</param>
/// <param name="creditProgramClients">кредитные программы клиента</param>
public class ClientDataModel(string id, string name, string surname, decimal balance, string clerkId,
List<DepositClientDataModel> depositClients, List<ClientCreditProgramDataModel> creditProgramClients) : IValidation
{
public string Id { get; private set; } = id;
@@ -22,7 +25,11 @@ public class ClientDataModel(string id, string name, string surname, decimal bal
public decimal Balance { get; private set; } = balance;
public string Clerkid { get; private set; } = clerkId;
public string ClerkId { get; private set; } = clerkId;
public List<DepositClientDataModel> DepositClients { get; private set; } = depositClients;
public List<ClientCreditProgramDataModel> CreditProgramClients { get; private set; } = creditProgramClients;
public void Validate()
{
@@ -46,13 +53,21 @@ public class ClientDataModel(string id, string name, string surname, decimal bal
{
throw new ValidationException("Field Balance is less or equal to zero");
}
if (Clerkid.IsEmpty())
if (ClerkId.IsEmpty())
{
throw new ValidationException("Field Clerkid is null or empty");
}
if (!Clerkid.IsGuid())
if (!ClerkId.IsGuid())
{
throw new ValidationException("The value in the field Clerkid is not a unique identifier");
}
if (DepositClients is null)
{
throw new ValidationException("Field DepositClients is null");
}
if (CreditProgramClients is null)
{
throw new ValidationException("Field CreditProgramClients is null");
}
}
}

View File

@@ -0,0 +1,10 @@
namespace BankContracts.DataModels;
public class ClientsByCreditProgramDataModel
{
public required string CreditProgramId { get; set; }
public required string CreditProgramName { get; set; }
public required List<string> ClientSurname { get; set; }
public required List<string> ClientName { get; set; }
public required List<decimal> ClientBalance { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace BankContracts.DataModels;
public class ClientsByDepositDataModel
{
public required string ClientSurname { get; set; }
public required string ClientName { get; set; }
public required decimal ClientBalance { get; set; }
public required float DepositRate { get; set; }
public required int DepositPeriod { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace BankContracts.DataModels;
public class CreditProgramAndDepositByCurrencyDataModel
{
public required string CurrencyName { get; set; }
public required List<string> CreditProgramName { get; set; }
public required List<int> CreditProgramMaxCost { get; set; }
public required List<float> DepositRate { get; set; }
public required List<int> DepositPeriod { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -13,7 +13,9 @@ namespace BankContracts.DataModels;
/// <param name="maxCost">максимальная сумма</param>
/// <param name="storekeeperId">уникальный Guid Индентификатор кладовщика</param>
/// <param name="periodId">уникальный Guid Индентификатор срока</param>
public class CreditProgramDataModel(string id, string name, decimal cost, decimal maxCost, string storekeeperId, string periodId) : IValidation
/// <param name="currencyCreditPrograms">валюты кредитной программы</param>
public class CreditProgramDataModel(string id, string name, decimal cost, decimal maxCost, string storekeeperId, string periodId,
List<CreditProgramCurrencyDataModel> currencyCreditPrograms) : IValidation
{
public string Id { get; private set; } = id;
@@ -27,6 +29,8 @@ public class CreditProgramDataModel(string id, string name, decimal cost, decima
public string PeriodId { get; private set; } = periodId;
public List<CreditProgramCurrencyDataModel> Currencies { get; private set; } = currencyCreditPrograms;
public void Validate()
{
if (Id.IsEmpty())
@@ -65,5 +69,9 @@ public class CreditProgramDataModel(string id, string name, decimal cost, decima
{
throw new ValidationException("The value in the field PeriodId is not a unique identifier");
}
if (Currencies is null)
{
throw new ValidationException("Field Currencies is null");
}
}
}

View File

@@ -0,0 +1,9 @@
namespace BankContracts.DataModels;
public class DepositByCreditProgramDataModel
{
public required string CreditProgramName { get; set; }
public required List<float> DepositRate { get; set; }
public required List<decimal> DepositCost { get; set; }
public required List<int> DepositPeriod { get; set; }
}

View File

@@ -12,7 +12,9 @@ namespace BankContracts.DataModels;
/// <param name="cost">стоимость</param>
/// <param name="period">срок</param>
/// <param name="clerkId">уникальный Guid индентификатор клерка</param>
public class DepositDataModel(string id, float interestRate, decimal cost, int period, string clerkId) : IValidation
/// <param name="depositCurrencies">валюты вклада</param>
public class DepositDataModel(string id, float interestRate, decimal cost, int period, string clerkId,
List<DepositCurrencyDataModel> depositCurrencies) : IValidation
{
public string Id { get; private set; } = id;
@@ -24,6 +26,8 @@ public class DepositDataModel(string id, float interestRate, decimal cost, int p
public string ClerkId { get; private set; } = clerkId;
public List<DepositCurrencyDataModel> Currencies { get; private set; } = depositCurrencies;
public void Validate()
{
if (Id.IsEmpty())
@@ -54,5 +58,9 @@ public class DepositDataModel(string id, float interestRate, decimal cost, int p
{
throw new ValidationException("The value in the field ClerkId is not a unique identifier");
}
if (Currencies is null)
{
throw new ValidationException("Field Currencies is null");
}
}
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.Exceptions;
/// <summary>
/// Исключение при работе с хранилищем
/// при добавлении существующего элемента
/// </summary>
/// <param name="message">сообщение</param>
public class ElementExistsException(string message) : Exception(message) { }

View File

@@ -0,0 +1,7 @@
namespace BankContracts.Exceptions;
/// <summary>
/// Исключение для не найденного элемента в бизнес логике и контрактах
/// </summary>
/// <param name="message"></param>
public class ElementNotFoundException(string message) : Exception(message) { }

View File

@@ -0,0 +1,8 @@
namespace BankContracts.Exceptions;
/// <summary>
/// Исключения для валидации дат
/// </summary>
/// <param name="from">начальная дата</param>
/// <param name="to">конечная дата</param>
public class IncorrectDatesException(DateTime from, DateTime to) : Exception($"date: {from} is older than {to}") { }

View File

@@ -0,0 +1,7 @@
namespace BankContracts.Exceptions;
/// <summary>
/// Исключение для null списка
/// </summary>
/// <param name="message">сообщение</param>
public class NullListException(string message) : Exception(message) { }

View File

@@ -0,0 +1,7 @@
namespace BankContracts.Exceptions;
/// <summary>
/// Исключение при работе с хранилищем
/// </summary>
/// <param name="message"></param>
public class StorageException(string message) : Exception(message) { }

View File

@@ -0,0 +1,12 @@
namespace BankContracts.Extensions;
/// <summary>
/// расширение для проверки дат
/// </summary>
public static class DateTimeExtensions
{
public static bool IsDateNotOlder(this DateTime date, DateTime olderDate)
{
return date >= olderDate;
}
}

View File

@@ -0,0 +1,10 @@
namespace BankContracts.Infrastructure;
/// <summary>
/// интерфейс для подключения к бд
/// хз зачем он нужен тут в контрактах а не в самой бд
/// </summary>
public interface IConfigurationDatabase
{
public string ConnectionString { get; }
}

View File

@@ -0,0 +1,65 @@
using System.Net;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace BankContracts.Infrastructure;
/// <summary>
/// класс для http ответов
/// </summary>
public class OperationResponse
{
protected HttpStatusCode StatusCode { get; set; }
protected object? Result { get; set; }
protected string? FileName { get; set; }
public IActionResult GetResponse(HttpRequest request, HttpResponse response)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(response);
response.StatusCode = (int)StatusCode;
if (Result is null)
{
return new StatusCodeResult((int)StatusCode);
}
if (Result is Stream stream)
{
return new FileStreamResult(stream, "application/octet-stream")
{
FileDownloadName = FileName
};
}
return new ObjectResult(Result);
}
protected static TResult OK<TResult, TData>(TData data)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.OK, Result = data };
protected static TResult OK<TResult, TData>(TData data, string fileName) where TResult : OperationResponse,
new() => new() { StatusCode = HttpStatusCode.OK, Result = data, FileName = fileName };
protected static TResult NoContent<TResult>()
where TResult : OperationResponse, new() => new() { StatusCode = HttpStatusCode.NoContent };
protected static TResult BadRequest<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.BadRequest, Result = errorMessage };
protected static TResult NotFound<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.NotFound, Result = errorMessage };
protected static TResult InternalServerError<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.InternalServerError, Result = errorMessage };
protected static TResult Unauthorized<TResult>(string? errorMessage = null)
where TResult : OperationResponse, new() =>
new() { StatusCode = HttpStatusCode.Unauthorized, Result = errorMessage };
}

View File

@@ -4,7 +4,9 @@ namespace BankContracts.StorageContracts;
public interface IClientStorageContract
{
List<ClientDataModel> GetList();
List<ClientDataModel> GetList(string? clerkId = null);
Task<List<ClientDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
ClientDataModel? GetElementById(string id);

View File

@@ -4,7 +4,9 @@ namespace BankContracts.StorageContracts;
public interface ICreditProgramStorageContract
{
List<CreditProgramDataModel> GetList();
List<CreditProgramDataModel> GetList(string? storekeeperId = null, string? periodId = null);
Task<List<CreditProgramDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
CreditProgramDataModel? GetElementById(string id);

View File

@@ -4,7 +4,9 @@ namespace BankContracts.StorageContracts;
public interface ICurrencyStorageContract
{
List<CurrencyDataModel> GetList();
List<CurrencyDataModel> GetList(string? storekeeperId = null);
Task<List<CurrencyDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
CurrencyDataModel? GetElementById(string id);

View File

@@ -4,11 +4,13 @@ namespace BankContracts.StorageContracts;
public interface IDepositStorageContract
{
List<DepositDataModel> GetList();
List<DepositDataModel> GetList(string? clerkId = null);
Task<List<DepositDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct);
DepositDataModel? GetElementById(string id);
DepositDataModel? GetElementByName(string name);
DepositDataModel? GetElementByInterestRate(float interestRate);
void AddElement(DepositDataModel depositDataModel);

View File

@@ -4,7 +4,7 @@ namespace BankContracts.StorageContracts;
public interface IPeriodStorageContract
{
List<PeriodDataModel> GetList(DateTime startDate, DateTime endDate);
List<PeriodDataModel> GetList(DateTime? startDate = null, DateTime? endDate = null, string? storekeeperId = null);
PeriodDataModel? GetElementById(string id);

View File

@@ -4,7 +4,7 @@ namespace BankContracts.StorageContracts;
public interface IReplenishmentStorageContract
{
List<ReplenishmentDataModel> GetList();
List<ReplenishmentDataModel> GetList(DateTime? fromDate = null, DateTime? toDate = null, string? clerkId = null, string? depositId = null);
ReplenishmentDataModel? GetElementById(string id);

View File

@@ -0,0 +1,23 @@
namespace BankContracts.ViewModels;
/// <summary>
/// модель представления для клерка
/// </summary>
public class ClerkViewModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string Surname { get; set; }
public required string MiddleName { get; set; }
public required string Login { get; set; }
public required string Password { get; set; }
public required string Email { get; set; }
public required string PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.ViewModels;
public class ClientCreditProgramViewModel
{
public required string? CreditProgramId { get; set; }
public required string? ClientId { get; set; }
}

View File

@@ -0,0 +1,18 @@
namespace BankContracts.ViewModels;
public class ClientViewModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string Surname { get; set; }
public required decimal Balance { get; set; }
public required string ClerkId { get; set; }
public required List<DepositClientViewModel> DepositClients { get; set; }
public required List<ClientCreditProgramViewModel>? CreditProgramClients { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace BankContracts.ViewModels;
public class ClientsByCreditProgramViewModel
{
public required string CreditProgramName { get; set; }
public required List<string> ClientSurname { get; set; }
public required List<string> ClientName { get; set; }
public required List<decimal> ClientBalance { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace BankContracts.ViewModels;
public class ClientsByDepositViewModel
{
public required string ClientSurname { get; set; }
public required string ClientName { get; set; }
public required decimal ClientBalance { get; set; }
public required float DepositRate { get; set; }
public required int DepositPeriod { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace BankContracts.ViewModels;
public class CreditProgramAndDepositByCurrencyViewModel
{
public required string CurrencyName { get; set; }
public required List<string> CreditProgramName { get; set; }
public required List<int> CreditProgramMaxCost { get; set; }
public required List<float> DepositRate { get; set; }
public required List<int> DepositPeriod { get; set; }
public DateTime FromPeriod { get; set; }
public DateTime ToPeriod { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.ViewModels;
public class CreditProgramCurrencyViewModel
{
public required string CreditProgramId { get; set; }
public required string CurrencyId { get; set; }
}

View File

@@ -0,0 +1,21 @@
namespace BankContracts.ViewModels;
/// <summary>
/// модель представления для кредитной программы
/// </summary>
public class CreditProgramViewModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public decimal Cost { get; set; }
public decimal MaxCost { get; set; }
public required string StorekeeperId { get; set; }
public required string PeriodId { get; set; }
public required List<CreditProgramCurrencyViewModel>? CurrencyCreditPrograms { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace BankContracts.ViewModels;
public class CurrencyViewModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string Abbreviation { get; set; }
public required decimal Cost { get; set; }
public required string StorekeeperId { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace BankContracts.ViewModels;
public class DepositByCreditProgramViewModel
{
public required string CreditProgramName { get; set; }
public required List<float> DepositRate { get; set; }
public required List<decimal> DepositCost { get; set; }
public required List<int> DepositPeriod { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.ViewModels;
public class DepositClientViewModel
{
public required string DepositId { get; set; }
public required string ClientId { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace BankContracts.ViewModels;
public class DepositCurrencyViewModel
{
public required string DepositId { get; set; }
public required string CurrencyId { get; set; }
}

View File

@@ -0,0 +1,22 @@
using BankContracts.BindingModels;
namespace BankContracts.ViewModels;
/// <summary>
/// модель представления для вклада
/// </summary>
public class DepositViewModel
{
public required string Id { get; set; }
public required float InterestRate { get; set; }
public required decimal Cost { get; set; }
public required int Period { get; set; }
public required string ClerkId { get; set; }
public required List<DepositCurrencyBindingModel>? DepositCurrencies { get; set; }
}

View File

@@ -0,0 +1,15 @@
namespace BankContracts.ViewModels;
/// <summary>
/// модель представления для срока
/// </summary>
public class PeriodViewModel
{
public required string Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public required string StorekeeperId { get; set; }
}

View File

@@ -0,0 +1,14 @@
namespace BankContracts.ViewModels;
public class ReplenishmentViewModel
{
public required string Id { get; set; }
public required decimal Amount { get; set; }
public required DateTime Date { get; set; }
public required string DepositId { get; set; }
public required string ClerkId { get; set; }
}

View File

@@ -0,0 +1,20 @@
namespace BankContracts.ViewModels;
public class StorekeeperViewModel
{
public required string Id { get; set; }
public required string Name { get; set; }
public required string Surname { get; set; }
public required string MiddleName { get; set; }
public required string Login { get; set; }
public required string Password { get; set; }
public required string Email { get; set; }
public required string PhoneNumber { get; set; }
}

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BankContracts\BankContracts.csproj" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="BankTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="BankWebApi" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,103 @@
using BankContracts.Infrastructure;
using BankDatabase.Models;
using Microsoft.EntityFrameworkCore;
namespace BankDatabase;
internal class BankDbContext(IConfigurationDatabase configurationDatabase) : DbContext
{
private readonly IConfigurationDatabase? _configurationDatabase = configurationDatabase;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql(_configurationDatabase?.ConnectionString, o => o.SetPostgresVersion(14, 2));
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Clerk>()
.HasIndex(x => x.Login)
.IsUnique();
modelBuilder.Entity<Clerk>()
.HasIndex(x => x.Email)
.IsUnique();
modelBuilder.Entity<Clerk>()
.HasIndex(x => x.PhoneNumber)
.IsUnique();
modelBuilder.Entity<CreditProgram>()
.HasIndex(x => x.Name)
.IsUnique();
modelBuilder.Entity<Currency>()
.HasIndex(x => x.Abbreviation)
.IsUnique();
modelBuilder.Entity<Storekeeper>()
.HasIndex(x => x.PhoneNumber)
.IsUnique();
modelBuilder.Entity<Storekeeper>()
.HasIndex(x => x.Email)
.IsUnique();
modelBuilder.Entity<Storekeeper>()
.HasIndex(x => x.Login)
.IsUnique();
modelBuilder.Entity<Clerk>()
.HasMany(x => x.Deposits)
.WithOne(x => x.Clerk)
.HasForeignKey(x => x.ClerkId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Clerk>()
.HasMany(x => x.Clients)
.WithOne(x => x.Clerk)
.HasForeignKey(x => x.ClerkId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Clerk>()
.HasMany(x => x.Replenishments)
.WithOne(x => x.Clerk)
.HasForeignKey(x => x.ClerkId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<DepositCurrency>().HasKey(x => new { x.DepositId, x.CurrencyId });
modelBuilder.Entity<ClientCreditProgram>().HasKey(x => new { x.ClientId, x.CreditProgramId });
modelBuilder.Entity<DepositClient>().HasKey(x => new { x.DepositId, x.ClientId });
modelBuilder.Entity<CreditProgramCurrency>().HasKey(x => new { x.CreditProgramId, x.CurrencyId });
}
public DbSet<Clerk> Clerks { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<CreditProgram> CreditPrograms { get; set; }
public DbSet<Currency> Currencies { get; set; }
public DbSet<Deposit> Deposits { get; set; }
public DbSet<Period> Periods { get; set; }
public DbSet<Replenishment> Replenishments { get; set; }
public DbSet<Storekeeper> Storekeepers { get; set; }
public DbSet<DepositCurrency> DepositCurrencies { get; set; }
public DbSet<ClientCreditProgram> CreditProgramClients { get; set; }
public DbSet<DepositClient> DepositClients { get; set; }
public DbSet<CreditProgramCurrency> CurrencyCreditPrograms { get; set; }
}

View File

@@ -0,0 +1,17 @@
using BankContracts.Infrastructure;
using Microsoft.EntityFrameworkCore.Design;
namespace BankDatabase;
//public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<BankDbContext>
//{
// //public BankDbContext CreateDbContext(string[] args)
// //{
// // return new BankDbContext(new ConfigurationDatabase());
// //}
//}
internal class ConfigurationDatabase : IConfigurationDatabase
{
public string ConnectionString => "Host=127.0.0.1;Port=5432;Database=BankTest;Username=postgres;Password=admin123;";
}

View File

@@ -0,0 +1,151 @@
using AutoMapper;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.StorageContracts;
using BankDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace BankDatabase.Implementations;
/// <summary>
/// реализация контракта для клерка
/// </summary>
internal class ClerkStorageContract : IClerkStorageContract
{
private readonly BankDbContext _dbContext;
private readonly Mapper _mapper;
public ClerkStorageContract(BankDbContext dbContext)
{
_dbContext = dbContext;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Clerk, ClerkDataModel>();
cfg.CreateMap<ClerkDataModel, Clerk>();
});
_mapper = new Mapper(config);
}
public List<ClerkDataModel> GetList()
{
try
{
return [.. _dbContext.Clerks.Select(x => _mapper.Map<ClerkDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClerkDataModel? GetElementById(string id)
{
try
{
return _mapper.Map<ClerkDataModel>(GetClerkById(id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClerkDataModel? GetElementByLogin(string login)
{
try
{
return _mapper.Map<ClerkDataModel>(_dbContext.Clerks.FirstOrDefault(x => x.Login == login));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClerkDataModel? GetElementByPhoneNumber(string phoneNumber)
{
try
{
return _mapper.Map<ClerkDataModel>(_dbContext.Clerks.FirstOrDefault(x => x.PhoneNumber == phoneNumber));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void AddElement(ClerkDataModel clerkDataModel)
{
try
{
_dbContext.Clerks.Add(_mapper.Map<Clerk>(clerkDataModel));
_dbContext.SaveChanges();
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "PK_Clerks" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Id {clerkDataModel.Id}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_Email" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Email {clerkDataModel.Email}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_PhoneNumber" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"PhoneNumber {clerkDataModel.PhoneNumber}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_Login" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Login {clerkDataModel.Login}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void UpdElement(ClerkDataModel clerkDataModel)
{
try
{
var element = GetClerkById(clerkDataModel.Id) ?? throw new ElementNotFoundException(clerkDataModel.Id);
_dbContext.Clerks.Update(_mapper.Map(clerkDataModel, element));
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_Email" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Email {clerkDataModel.Email}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_PhoneNumber" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"PhoneNumber {clerkDataModel.PhoneNumber}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clerks_Login" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Login {clerkDataModel.Login}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
private Clerk? GetClerkById(string id) => _dbContext.Clerks.Where(x => x.Id == id).FirstOrDefault();
}

View File

@@ -0,0 +1,219 @@
using AutoMapper;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.StorageContracts;
using BankDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace BankDatabase.Implementations;
internal class ClientStorageContract : IClientStorageContract
{
private readonly BankDbContext _dbContext;
private readonly Mapper _mapper;
public ClientStorageContract(BankDbContext dbContext)
{
_dbContext = dbContext;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Clerk, ClerkDataModel>();
cfg.CreateMap<Client, ClientDataModel>()
.ForMember(dest => dest.DepositClients, opt => opt.MapFrom(src => src.DepositClients))
.ForMember(dest => dest.CreditProgramClients, opt => opt.MapFrom(src => src.CreditProgramClients));
cfg.CreateMap<ClientDataModel, Client>()
.ForMember(dest => dest.DepositClients, opt => opt.MapFrom(src => src.DepositClients))
.ForMember(dest => dest.CreditProgramClients, opt => opt.MapFrom(src => src.CreditProgramClients));
cfg.CreateMap<DepositClient, DepositClientDataModel>()
.ForMember(dest => dest.DepositId, opt => opt.MapFrom(src => src.DepositId))
.ForMember(dest => dest.ClientId, opt => opt.MapFrom(src => src.ClientId));
cfg.CreateMap<DepositClientDataModel, DepositClient>()
.ForMember(dest => dest.DepositId, opt => opt.MapFrom(src => src.DepositId))
.ForMember(dest => dest.ClientId, opt => opt.MapFrom(src => src.ClientId));
cfg.CreateMap<Deposit, DepositClientDataModel>()
.ForMember(dest => dest.DepositId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.ClientId, opt => opt.Ignore());
cfg.CreateMap<DepositClientDataModel, Deposit>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.DepositId));
cfg.CreateMap<ClientCreditProgram, ClientCreditProgramDataModel>()
.ForMember(dest => dest.ClientId, opt => opt.MapFrom(src => src.ClientId))
.ForMember(dest => dest.CreditProgramId, opt => opt.MapFrom(src => src.CreditProgramId));
cfg.CreateMap<ClientCreditProgramDataModel, ClientCreditProgram>()
.ForMember(dest => dest.ClientId, opt => opt.MapFrom(src => src.ClientId))
.ForMember(dest => dest.CreditProgramId, opt => opt.MapFrom(src => src.CreditProgramId));
cfg.CreateMap<CreditProgram, ClientCreditProgramDataModel>()
.ForMember(dest => dest.CreditProgramId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.ClientId, opt => opt.Ignore());
cfg.CreateMap<ClientCreditProgramDataModel, CreditProgram>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.CreditProgramId));
cfg.CreateMap<Replenishment, ReplenishmentDataModel>();
});
_mapper = new Mapper(config);
}
public List<ClientDataModel> GetList(string? clerkId = null)
{
try
{
var query = _dbContext.Clients
.Include(x => x.Clerk)
.Include(x => x.CreditProgramClients)
.Include(x => x.DepositClients)
.AsQueryable();
if (clerkId is not null)
{
query = query.Where(x => x.ClerkId == clerkId);
}
return [.. query.Select(x => _mapper.Map<ClientDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClientDataModel? GetElementById(string id)
{
try
{
return _mapper.Map<ClientDataModel>(_dbContext.Clients.FirstOrDefault(x => x.Id == id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClientDataModel? GetElementByName(string name)
{
try
{
return _mapper.Map<ClientDataModel>(_dbContext.Clients.Include(x => x.Clerk).FirstOrDefault(x => x.Name == name));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public ClientDataModel? GetElementBySurname(string surname)
{
try
{
return _mapper.Map<ClientDataModel>(_dbContext.Clients.Include(x => x.Clerk).FirstOrDefault(x => x.Surname == surname));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void AddElement(ClientDataModel clientDataModel)
{
try
{
_dbContext.Clients.Add(_mapper.Map<Client>(clientDataModel));
_dbContext.SaveChanges();
}
catch (InvalidOperationException ex) when (ex.TargetSite?.Name == "ThrowIdentityConflict")
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Id {clientDataModel.Id}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clients_Name" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Name {clientDataModel.Name}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clients_Surname" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Surname {clientDataModel.Surname}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public async Task<List<ClientDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
var clients = await _dbContext.Clients
.Include(x => x.Clerk)
.ToListAsync(ct);
return clients.Select(x => _mapper.Map<ClientDataModel>(x)).ToList();
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void UpdElement(ClientDataModel clientDataModel)
{
try
{
var transaction = _dbContext.Database.BeginTransaction();
try
{
var element = GetClientById(clientDataModel.Id) ?? throw new ElementNotFoundException(clientDataModel.Id);
//проверь пожалуйста(не уверен)
if (clientDataModel.DepositClients != null && clientDataModel.CreditProgramClients != null)
{
if (element.DepositClients != null || element.DepositClients?.Count >= 0)
{
_dbContext.DepositClients.RemoveRange(element.DepositClients);
}
if (element.CreditProgramClients != null || element.CreditProgramClients?.Count >= 0)
{
_dbContext.CreditProgramClients.RemoveRange(element.CreditProgramClients);
}
element.DepositClients = _mapper.Map<List<DepositClient>>(clientDataModel.DepositClients);
element.CreditProgramClients = _mapper.Map<List<ClientCreditProgram>>(clientDataModel.CreditProgramClients);
}
_mapper.Map(element, clientDataModel);
_dbContext.SaveChanges();
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clients_Name" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Name {clientDataModel.Name}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Clients_Surname" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Surname {clientDataModel.Surname}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
private Client? GetClientById(string id) => _dbContext.Clients.Include(client => client.DepositClients).Include(client => client.CreditProgramClients).FirstOrDefault(x => x.Id == id);
}

View File

@@ -0,0 +1,145 @@
using AutoMapper;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.StorageContracts;
using BankDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace BankDatabase.Implementations;
/// <summary>
/// реализация контракта хранилища для кредитной программы
/// </summary>
internal class CreditProgramStorageContract : ICreditProgramStorageContract
{
private readonly BankDbContext _dbContext;
private readonly Mapper _mapper;
public CreditProgramStorageContract(BankDbContext dbContext)
{
_dbContext = dbContext;
var config = new MapperConfiguration(x =>
{
x.CreateMap<CreditProgram, CreditProgramDataModel>()
.ForMember(dest => dest.Currencies, opt => opt.MapFrom(src => src.CurrencyCreditPrograms));
x.CreateMap<CreditProgramDataModel, CreditProgram>()
.ForMember(dest => dest.CurrencyCreditPrograms, opt => opt.MapFrom(src => src.Currencies));
x.CreateMap<CreditProgramCurrency, CreditProgramCurrencyDataModel>()
.ForMember(dest => dest.CreditProgramId, opt => opt.MapFrom(src => src.CreditProgramId))
.ForMember(dest => dest.CurrencyId, opt => opt.MapFrom(src => src.CurrencyId));
x.CreateMap<CreditProgramCurrencyDataModel, CreditProgramCurrency>()
.ForMember(dest => dest.CreditProgramId, opt => opt.MapFrom(src => src.CreditProgramId))
.ForMember(dest => dest.CurrencyId, opt => opt.MapFrom(src => src.CurrencyId))
.ForMember(dest => dest.CreditProgram, opt => opt.Ignore())
.ForMember(dest => dest.Currency, opt => opt.Ignore());
});
_mapper = new Mapper(config);
}
public List<CreditProgramDataModel> GetList(string? storekeeperId = null, string? periodId = null)
{
try
{
var query = _dbContext.CreditPrograms
.Include(x => x.CurrencyCreditPrograms)
.AsQueryable();
if (storekeeperId is not null)
{
query = query.Where(x => x.StorekeeperId == storekeeperId);
}
if (periodId is not null)
{
query = query.Where(x => x.PeriodId == periodId);
}
return [.. query.Select(x => _mapper.Map<CreditProgramDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public async Task<List<CreditProgramDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
var query = _dbContext.CreditPrograms.AsQueryable();
//query = query.Where(x => x.CreatedDate >= startDate && x.CreatedDate <= endDate);
var creditPrograms = await query.ToListAsync(ct);
return creditPrograms.Select(x => _mapper.Map<CreditProgramDataModel>(x)).ToList();
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public CreditProgramDataModel? GetElementById(string id)
{
try
{
return _mapper.Map<CreditProgramDataModel>(GetCreditProgramById(id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void AddElement(CreditProgramDataModel creditProgramDataModel)
{
try
{
_dbContext.CreditPrograms.Add(_mapper.Map<CreditProgram>(creditProgramDataModel));
_dbContext.SaveChanges();
}
catch (InvalidOperationException ex) when (ex.TargetSite?.Name == "ThrowIdentityConflict")
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Id {creditProgramDataModel.Id}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_CreditPrograms_Name" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"PhoneNumber {creditProgramDataModel.Name}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void UpdElement(CreditProgramDataModel creditProgramDataModel)
{
try
{
var element = GetCreditProgramById(creditProgramDataModel.Id) ?? throw new ElementNotFoundException($"id: {creditProgramDataModel.Id}");
_dbContext.CreditPrograms.Update(_mapper.Map(creditProgramDataModel, element));
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_CreditPrograms_Name" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"PhoneNumber {creditProgramDataModel.Name}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
private CreditProgram? GetCreditProgramById(string id) => _dbContext.CreditPrograms.FirstOrDefault(x => x.Id == id);
}

View File

@@ -0,0 +1,140 @@
using AutoMapper;
using BankContracts.DataModels;
using BankContracts.Exceptions;
using BankContracts.StorageContracts;
using BankDatabase.Models;
using Microsoft.EntityFrameworkCore;
using Npgsql;
namespace BankDatabase.Implementations;
/// <summary>
/// реализация контракта хранилища для валюты
/// </summary>
internal class CurrencyStorageContract : ICurrencyStorageContract
{
private readonly BankDbContext _dbContext;
private readonly Mapper _mapper;
public CurrencyStorageContract(BankDbContext dbContext)
{
_dbContext = dbContext;
var config = new MapperConfiguration(x =>
{
x.CreateMap<CurrencyDataModel, Currency>();
x.CreateMap<Currency, CurrencyDataModel>();
});
_mapper = new Mapper(config);
}
public List<CurrencyDataModel> GetList(string? storekeeperId = null)
{
try
{
var query = _dbContext.Currencies.AsQueryable();
if (storekeeperId is not null)
{
query = query.Where(x => x.StorekeeperId == storekeeperId);
}
return [.. query.Select(x => _mapper.Map<CurrencyDataModel>(x))];
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public async Task<List<CurrencyDataModel>> GetListAsync(DateTime startDate, DateTime endDate, CancellationToken ct)
{
try
{
var query = _dbContext.Currencies.AsQueryable();
// query = query.Where(x => x.CreatedDate >= startDate && x.CreatedDate <= endDate);
var currencies = await query.ToListAsync(ct);
return currencies.Select(x => _mapper.Map<CurrencyDataModel>(x)).ToList();
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public CurrencyDataModel? GetElementById(string id)
{
try
{
return _mapper.Map<CurrencyDataModel>(GetCurrencyById(id));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public CurrencyDataModel? GetElementByAbbreviation(string abbreviation)
{
try
{
return _mapper.Map<CurrencyDataModel>(_dbContext.Currencies.FirstOrDefault(x => x.Abbreviation == abbreviation));
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void AddElement(CurrencyDataModel currencyDataModel)
{
try
{
_dbContext.Currencies.Add(_mapper.Map<Currency>(currencyDataModel));
_dbContext.SaveChanges();
}
catch (InvalidOperationException ex) when (ex.TargetSite?.Name == "ThrowIdentityConflict")
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Id {currencyDataModel.Id}");
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Currencies_Abbreviation" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Abbreviation {currencyDataModel.Abbreviation}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
public void UpdElement(CurrencyDataModel currencyDataModel)
{
try
{
var element = GetCurrencyById(currencyDataModel.Id) ?? throw new ElementNotFoundException($"id: {currencyDataModel.Id}");
_dbContext.Currencies.Update(_mapper.Map(currencyDataModel, element));
_dbContext.SaveChanges();
}
catch (ElementNotFoundException)
{
_dbContext.ChangeTracker.Clear();
throw;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException { ConstraintName: "IX_Currencies_Abbreviation" })
{
_dbContext.ChangeTracker.Clear();
throw new ElementExistsException($"Abbreviation {currencyDataModel.Name}");
}
catch (Exception ex)
{
_dbContext.ChangeTracker.Clear();
throw new StorageException(ex.Message);
}
}
private Currency? GetCurrencyById(string id) => _dbContext.Currencies.FirstOrDefault(x => x.Id == id);
}

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