From 75c4e7aeae6662bcfe22ea81d400abef563bdd94 Mon Sep 17 00:00:00 2001 From: ialbert Date: Wed, 1 Feb 2023 20:52:25 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-189 --- .../service/AccountBalanceService.java | 91 +++++++++++++++++++ .../balance/service/StatementService.java | 12 ++- .../ru/spcex/clearing/service/Clearing.java | 59 ++++++++++-- .../platform/enumeration/AccountType.java | 2 +- .../platform/messaging/domain/Consts.java | 1 + .../AccountBalanceClearingRequest.java | 38 ++++++++ 6 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/AccountBalanceClearingRequest.java diff --git a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/AccountBalanceService.java b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/AccountBalanceService.java index daa2280dc..4825ffb64 100644 --- a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/AccountBalanceService.java +++ b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/AccountBalanceService.java @@ -5,13 +5,18 @@ import org.springframework.stereotype.Component; import ru.clearing.classes.statics.data.account.Account; import ru.clearing.classes.statics.data.account.AccountBalance; import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; import ru.spcex.clearing.balance.validation.AccountBalanceValidation; import ru.spcex.clearing.balance.validation.ValidationStored; import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountBalanceClearingRequest; +import ru.spcex.platform.enumeration.AccountType; import ru.spcex.platform.enumeration.BalanceAccountType; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumKey; import ru.spcex.platform.utils.validation.IValidator; import java.math.BigDecimal; @@ -26,12 +31,14 @@ public class AccountBalanceService { private final ImdgProvider imdgProvider; private final Imdg accountBalanceImdg; private final Function validationFactory; + private final Imdg clearingCategoryImdg; public AccountBalanceService(ImdgProvider imdgProvider, @Qualifier("accountBalanceValidator") Function validationFactory) { this.imdgProvider = imdgProvider; this.validationFactory = validationFactory; this.accountBalanceImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class); + this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); } public AccountResult createAccountBalance(Long addresseeId, Long accountId, BigDecimal amount, @@ -85,4 +92,88 @@ public class AccountBalanceService { if (b == null) return a; return a.add(b); } + + public void updateAccountBalanceByClearing(AccountBalanceClearingRequest req) { + ClearingCategory category = getClearingCategoryByCompanyId(req.getCompanyId()); + if (category.equals(ClearingCategory.I)) { + updateAccountCategoryIClrn(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount()); + updateAccountCategoryITran(req.getFirstLegAmount()); + } else if (category.equals(ClearingCategory.V)) { + updateAccountCategoryVInfo(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount()); + updateAccountCategoryVAnlt(req.getFirstLegAmount()); + } else if (category.equals(ClearingCategory.B)) { + updateAccountCategoryBClrn(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount()); + } + //todo send message success to Kafka + } + + private ClearingCategory getClearingCategoryByCompanyId(Long companyId) { + ClearingMemberCategory category = clearingCategoryImdg.getSingleObjectByFieldValues( + Map.of("companyId", companyId)); + return IEnumKey.getEnumByKey(ClearingCategory.class, + category.getClearingMemberCategory()); + } + + private void updateAccountCategoryIClrn(Long accountId, Long companyId, BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Clrn); + BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount(); + BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount); + accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setBalanceAmount(resultFreeBalanceAmount); + accountBalance.setUpdated(Instant.now()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryITran(BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(AccountType.Tran); + BigDecimal freeBalanceAmount = accountBalance.getFreeBalanceAmount(); + accountBalance.setFreeBalanceAmount(freeBalanceAmount.add(firstLegAmount.abs())); + accountBalance.setUpdated(Instant.now()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryVInfo(Long accountId, Long companyId, BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Info); + BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount(); + BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount); + accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setBalanceAmount(resultFreeBalanceAmount); + accountBalance.setUpdated(Instant.now()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryVAnlt(BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(AccountType.Anlt); + BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount(); + BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount); + accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount); + accountBalance.setBalanceAmount(resultFreeBalanceAmount); + accountBalance.setUpdated(Instant.now()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryBClrn(Long accountId, Long companyId, BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Clrn); + BigDecimal freeBalanceAmount = accountBalance.getFreeBalanceAmount(); + accountBalance.setDebitAmount(plus(freeBalanceAmount, firstLegAmount.abs())); + accountBalance.setUpdated(Instant.now()); + accountBalanceImdg.update(accountBalance); + } + + private AccountBalance loadAccountBalance(Long accountId, Long companyId, AccountType type) { + return accountBalanceImdg.getSingleObjectByFieldValues( + Map.of("accountId", accountId, "companyId", companyId, + "accountType", type.getKey()) + ); + } + + private AccountBalance loadAccountBalance(AccountType type) { + return accountBalanceImdg.getSingleObjectByFieldValues( + Map.of("companyId", 1L, + "accountType", type.getKey()) + ); + } } diff --git a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/StatementService.java b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/StatementService.java index 1cc0e0575..9bedcb348 100644 --- a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/StatementService.java +++ b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/service/StatementService.java @@ -15,6 +15,7 @@ import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdf01Request; import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdfRequestPart; +import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountBalanceClearingRequest; import ru.spcex.clearing.platform.messaging.domain.cud.balance.ExportToFileRequest; import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; @@ -38,14 +39,16 @@ public class StatementService extends QueueConsumer implements InitializingBean private final KafkaSender kafkaReqProducer; private final Map> sdfImdgs; private final Map> executorsMap; + private final AccountBalanceService accountBalanceService; @Autowired public StatementService(Consumer kafkaQueue, ImdgProvider imdgProvider, KafkaSender kafkaReqProducer, - @Qualifier("sdfExecutors") Map> executorsMap) { + @Qualifier("sdfExecutors") Map> executorsMap, AccountBalanceService accountBalanceService) { super(kafkaQueue); this.imdgProvider = imdgProvider; + this.accountBalanceService = accountBalanceService; this.sdfImdgs = new EnumMap<>(SdfTable.class); this.kafkaReqProducer = kafkaReqProducer; this.executorsMap = executorsMap; @@ -59,9 +62,16 @@ public class StatementService extends QueueConsumer implements InitializingBean callback(StatementRequest.class) .setConsumer(this::process) .forDestination(Consts.STATEMENT_PROCESS, callbacks::put); + callback(AccountBalanceClearingRequest.class) + .setConsumer(this::accountBalanceClearingUpdate) + .forDestination(Consts.BALANCE_ACCOUNT_UPDATE, callbacks::put); init(); } + private void accountBalanceClearingUpdate(BaseRequest updateAccBalanceReq) { + accountBalanceService.updateAccountBalanceByClearing(updateAccBalanceReq.getRequestPayload()); + } + private void process(BaseRequest systemRequest) { StatementRequest statementRequest = systemRequest.getRequestPayload(); Collection sdfGroup; diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Clearing.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Clearing.java index 3406f3383..d852259b0 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Clearing.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Clearing.java @@ -12,15 +12,13 @@ import ru.spcex.clearing.service.order.ExecutionDepositSorter; import ru.spcex.platform.enumeration.Allowed; import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgId; import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.validation.IValidator; import java.time.Instant; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.function.BiFunction; @Service @@ -30,6 +28,13 @@ public class Clearing { private final Imdg clearingCategoryImdg; private final ExecutionDepositSorter sorter; private final BiFunction validation; + private final ImdgId idProvider; + //при прохождении по выгруженным ExecutionDeposit, ошибочные статусы проставляются для + //контр сделок. В таком случае, в коллекции хранятся не синхронизированные с IMDG ExecutionDeposit + //для которых статус должен быть DENIED + //************ !!! NOT THREAD SAFE !!! ************ + private final Set deniedIds = new HashSet<>(); + private Long clearingSessionId; @Autowired public Clearing(ImdgProvider imdgProvider, @@ -37,11 +42,13 @@ public class Clearing { BiFunction validation) { this.executionDepositImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ExecutionDeposit, ExecutionDeposit.class); this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + this.idProvider = imdgProvider.getImdgIdGenerator(); this.sorter = sorter; this.validation = validation; } public void startClearing() { + clearingSessionId = idProvider.nextId(); //выгружаем ExecutionDeposit с пустым sessionId Collection execDeposits = executionDepositImdg.getCollectionObjectsBySQL("sessionId = null"); //делаем группировку ExecutionDeposit по категории и компании, делаем сортировку @@ -49,15 +56,29 @@ public class Clearing { executionDeposits.forEach((category, companies) -> { for (List companyExecDeposits : companies) { - processSingleCompany(category, companyExecDeposits); + runChecksSetStatus(category, companyExecDeposits); + } + }); + executionDeposits.forEach((category, companies) -> { + for (List companyExecDeposits : companies) { + processAllowedExecutionDeposit(category, companyExecDeposits); } }); } - private void processSingleCompany(ClearingCategory category, List executionDeposits) { - log.info("Start clearing for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId()); + //кладем в порядке I инициатор, V внутренний, B банк (ответная категория) + //получается I -> B; V -> B + // + //1) ТОЛЬКО ДЛЯ V: если DENIED по любым причинам то для всех следующих сделок этой компании DENIED + //2) DENIED сделка всегда встречная сделка тоже делается DENIED + //3) DENIED может быть для сделки которая уже прошла обработку и стала ALLOWED + private void runChecksSetStatus(ClearingCategory category, List executionDeposits) { + log.info("setting statuses for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId()); boolean financialError = false; for (ExecutionDeposit execDeposit : executionDeposits) { + if (deniedIds.contains(execDeposit.getId())) { + continue; + } if (financialError) { updateDenied(execDeposit); continue; @@ -80,6 +101,17 @@ public class Clearing { execDeposit.setCoverageStatus(Allowed.DENIED.getKey()); execDeposit.setUpdated(Instant.now()); executionDepositImdg.update(execDeposit); + //также необходимо установить статус DEND встречной сделке контрагента этой компании, которая выбирается из executionDeposit по ключу: + //securityId И чтобы сделка была компании категории clearingMemberCategory.clearingMemberCategory ≠ той категории, сделка которой обрабатывается в настоящий момент. + ExecutionDeposit matchedExecDeposit = executionDepositImdg.getSingleObjectBySQL( + "securityId = " + execDeposit.getSecurityId() + + " AND companyId != " + execDeposit.getCompanyId()); + if (matchedExecDeposit != null) { + matchedExecDeposit.setCoverageStatus(Allowed.DENIED.getKey()); + matchedExecDeposit.setUpdated(Instant.now()); + executionDepositImdg.update(matchedExecDeposit); + deniedIds.add(matchedExecDeposit.getId()); + } } private void updateAllowed(ExecutionDeposit execDeposit) { @@ -87,4 +119,17 @@ public class Clearing { execDeposit.setUpdated(Instant.now()); executionDepositImdg.update(execDeposit); } + + private void processAllowedExecutionDeposit(ClearingCategory category, List executionDeposits) { + log.info("running clearing for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId()); + for (ExecutionDeposit execDeposit : executionDeposits) { + if (deniedIds.contains(execDeposit.getId())) { + continue; + } + if (!Allowed.ALLOWED.getKey().equals(execDeposit.getCoverageStatus())) { + continue; + } + + } + } } diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/AccountType.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/AccountType.java index fa04d58a6..0cff3a8e3 100644 --- a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/AccountType.java +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/AccountType.java @@ -3,7 +3,7 @@ package ru.spcex.platform.enumeration; import ru.spcex.platform.utils.enumeration.IEnumKey; public enum AccountType implements IEnumKey { - Clrn("CLRN"), Bank("BANK"), Info("INFO"); + Clrn("CLRN"), Bank("BANK"), Info("INFO"), Tran("TRAN"), Anlt("ANLT"); private final String key; diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java index 552594f41..fd95ea1ad 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java @@ -50,6 +50,7 @@ public interface Consts { String EXPORT_PROCESS = "export-process"; String ACCOUNT_NEW = "account-new"; String BALANCE_ACCOUNT_NEW = "balance-account-new"; + String BALANCE_ACCOUNT_UPDATE = "balance-account-update"; String LAUNCHER_NEW = "launcher-new"; String NOTIFICATION_NEW = "notification-new"; diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/AccountBalanceClearingRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/AccountBalanceClearingRequest.java new file mode 100644 index 000000000..9748adff5 --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/AccountBalanceClearingRequest.java @@ -0,0 +1,38 @@ +package ru.spcex.clearing.platform.messaging.domain.cud.balance; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; + +public class AccountBalanceClearingRequest { + @JsonProperty + private Long accountId; + @JsonProperty + private Long companyId; + @JsonProperty + private BigDecimal firstLegAmount; + + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public Long getCompanyId() { + return companyId; + } + + public void setCompanyId(Long companyId) { + this.companyId = companyId; + } + + public BigDecimal getFirstLegAmount() { + return firstLegAmount; + } + + public void setFirstLegAmount(BigDecimal firstLegAmount) { + this.firstLegAmount = firstLegAmount; + } +} \ No newline at end of file