diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountService.java index 06a2da91b..66819ecaa 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountService.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountService.java @@ -19,6 +19,7 @@ import ru.spcex.clearing.platform.messaging.domain.cud.account.CorrespondentAcco import ru.spcex.clearing.platform.messaging.domain.cud.account.CorrespondentAccountUpdateRequest; 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.account.sdf01.AccountTerminationRequest; import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountSdfToStatementRequestPart; import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest; import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; @@ -34,6 +35,7 @@ import ru.spcex.platform.enumeration.ServiceStatus; import ru.spcex.platform.enumeration.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.ImdgTransaction; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.utils.enumeration.EnumMessage; @@ -50,6 +52,7 @@ import java.util.function.Function; @Service public class AccountService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); + private final ImdgProvider imdgProvider; private final Imdg accountMap; private final Imdg clearingMemberCategoryMap; private final Imdg relationMap; @@ -76,6 +79,7 @@ public class AccountService extends QueueConsumer implements InitializingBean { @Qualifier("correspondentAccountBlockRequestValidator") Function accountBlockRequestValidator) { super(kafkaQueue, kafkaProducer); + this.imdgProvider = imdgProvider; this.accountMap = imdgProvider.getImdg( IMDGDistributedNames.Map_Account, Account.class ); @@ -108,6 +112,10 @@ public class AccountService extends QueueConsumer implements InitializingBean { callback(CommonIdRequest.class) .setFunction(this::correspondentAccountBlock) .forDestination(Consts.DESTINATION_CORRESPONDENT_ACCOUNT_BLOCK, callbacks::put); + + callback(AccountTerminationRequest.class) + .setFunction(this::accountTerminationForCompany) + .forDestination(Consts.ACCOUNT_TERMINATION, callbacks::put); init(); } @@ -184,6 +192,48 @@ public class AccountService extends QueueConsumer implements InitializingBean { return null; } + + public RequestInfoUpdate accountTerminationForCompany(BaseRequest userRequest) { + log.debug("AccountTerminationRequest received, id={}", userRequest.getId()); + + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); + if (requestInfoUpdate != null) return requestInfoUpdate; // never - system + + AccountTerminationRequest req = userRequest.getRequestPayload(); + final Long companyId = req.getCompanyId(); + Collection accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId); + log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId); + int blockCount = 0; + ImdgTransaction tx = imdgProvider.newTransaction(); + boolean txOk = false; + try { + tx.beginTransaction(); + for (Account account : accounts) { + //Account account = accountMap.getSingleObjectByID(request.getId()); + if (!ServiceStatus.Blocked.equalsByKey(account.getStatus())) { + account.setStatus(ServiceStatus.Blocked.getKey()); + account.setUpdated(Instant.now()); + accountMap.update(account); + blockCount++; + } + } + txOk = true; + } finally { + if (txOk) + tx.commitTransaction(); + else + tx.beginTransaction(); + } + log.debug("For company {} block {} account (of {} account)", companyId, blockCount, accounts.size()); + + Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION_STEP2, req); + log.info("On termination request id={} send to next step {} new request id={}", + userRequest.getId(), Consts.ACCOUNT_TERMINATION_STEP2, reqId); + + log.debug("successfully processed, account termination"); + return null; + } + @Deprecated public RequestInfoUpdate accountNewSdf01(BaseRequest userRequest) { log.debug("AccountSdf01Request received"); diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java index 6c2180b2b..cde306470 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java @@ -19,7 +19,9 @@ import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; @Service public class AccountNotificationHelper { @@ -51,25 +53,22 @@ public class AccountNotificationHelper { } } log.debug("Sending messages to account-service for {} account", accounts.size()); - for (Account account : accounts) { - Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, account)); - log.debug("For account[{}] sended termination request id={}", account.getId(), reqId); - } + Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, null)); + log.debug("Send termination request id={}", reqId); return null; } - private AccountTerminationRequest createAccountsRequest(Long companyId, Account account) { - AccountTerminationRequest r = new AccountTerminationRequest(); - r.setCompanyId(companyId); - r.setAccount(account.getAccount()); - r.setAccountId(account.getId()); - return r; + private AccountTerminationRequest createAccountsRequest(Long companyId, Long terminationDocumentId) { + AccountTerminationRequest requestBatch = new AccountTerminationRequest(); + requestBatch.setCompanyId(companyId); + requestBatch.setTerminationDocumentId(terminationDocumentId); + return requestBatch; } - public CompanyErrors accountTerminationByDocumentNotification(Long companyId) { - Collection accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId); - log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId); + public CompanyErrors accountTerminationByDocumentNotification(Long companyId, Long terminationDocumentId) { +// Collection accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId); +// log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId); // for (Account account : accounts) { // Collection liabilities = liabilitiesClaimsAssetsMap.getCollectionObjectsBySQL("accountId=" + account.getId()); // for (LiabilitiesClaimsAssets liability : liabilities) { @@ -78,13 +77,12 @@ public class AccountNotificationHelper { // } // } // } - log.debug("Sending messages to account-service for {} account", accounts.size()); - for (Account account : accounts) { - Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, account)); - log.debug("For account[{}] send termination by document request id={}", account.getId(), reqId); - } - //todo group! - // ответ должен послаться в DESTINATION_COMPANY_BLOCK = "company-block"; +// log.debug("Sending messages to account-service for {} account", accounts.size()); + + AccountTerminationRequest requestBatch = createAccountsRequest(companyId, terminationDocumentId); + Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, requestBatch); + log.debug("For terminate account send termination by document request id={}", reqId); + // ответ должен послаться в ACCOUNT_TERMINATION_STEP2 = "company-block"; return null; } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java index ab0ac10ea..3cf059bc6 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java @@ -15,6 +15,7 @@ import ru.spcex.clearing.company.error.CompanyErrors; import ru.spcex.clearing.imdg.IMDGDistributedNames; 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.AccountTerminationRequest; import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; @@ -105,6 +106,10 @@ public class CompanyService extends QueueConsumer implements InitializingBean { callback(CompanyNewRequest.class) .setFunction(request -> requestHelper.requestFunction(this::updateCompany, request)) .forDestination(Consts.DESTINATION_COMPANY_UPDATE, callbacks::put); + + callback(AccountTerminationRequest.class) + .setFunction(request -> requestHelper.requestFunction(this::finishCompanyTermination, request)) + .forDestination(Consts.ACCOUNT_TERMINATION_STEP2, callbacks::put); init(); } @@ -373,4 +378,45 @@ public class CompanyService extends QueueConsumer implements InitializingBean { return null; } + + /** + * Блокировка компании после блокировки счетов + * + * @return + */ + private synchronized RequestInfoUpdate finishCompanyTermination(BaseRequest terminationRequest) throws ValidationException { + AccountTerminationRequest request = terminationRequest.getRequestPayload(); + log.debug("AccountTerminationRequest (block request) received id = {}", terminationRequest.getId()); + { // Валидация + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(terminationRequest); + if (requestInfoUpdate != null) return requestInfoUpdate; + } + + log.debug("Finish block company {}", request.getCompanyId()); + ImdgTransaction tx = imdgProvider.newTransaction(); + boolean txOk = false; + tx.beginTransaction(); + try { + Company company = companyIMap.getSingleObjectByID(request.getCompanyId()); + if (company == null) { + log.warn("Company {} not found", request.getCompanyId()); + //return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyNotFound); + } + if (WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus())) { + log.info("Company {} already blocked", company.getId()); + //return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyDisabled); + } else { + company.setWorkflowStatus(WorkflowStatus.Blocked.getKey()); + company.setUpdated(Instant.now()); + companyIMap.update(company); + } + + relationService.cancelAllRelationForCompany(tx, request.getCompanyId()); + txOk = true; + } finally { + if (txOk) tx.commitTransaction(); + else tx.rollbackTransaction(); + } + return null; + } } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java index f3ad1e08d..b23e3ff7f 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java @@ -249,14 +249,15 @@ public class ProfileDocumentService extends QueueConsumer implements Initializin existCompany.getWorkflowStatus(), existCompany.getId()); } - CompanyErrors hasError = accountNotificationHelper.accountTerminationByDocumentNotification(profileDocument.getCompanyId()); + CompanyErrors hasError = accountNotificationHelper.accountTerminationByDocumentNotification(profileDocument.getCompanyId(), profileDocument.getId()); if (hasError != null) { log.error("Error at termination account of company {} by document {}: {}", profileDocument.getCompanyId(), profileDocument.getId(), hasError + "(" + hasError.getId() + ")"); } log.debug("Now wait notification from account-service."); - /* todo processXCNT + /* processXCNT Дальнейшая обработка документа о расторжении, то есть обновление статусов в company и relation, возможна только после ответа об успешном обновлении статуса в account. + ACCOUNT_TERMINATION -> ACCOUNT_TERMINATION_STEP2 1.2. Обновить статус в бизнес-объекте company [profileDocument.companyId=company.id] согласно описанию с тегом "При расторжении". 1.3. Обновить статус в бизнес-объекте relation [profileDocument.companyId=relation.consumerId] согласно описанию с тегом "При расторжении". */ diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java index 21084def8..8ceb3bd34 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java @@ -137,7 +137,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { Company company = companyMap.getSingleObjectByID(req.getCompanyId()); - Relation existRelation = searchRelation(req.getClearingMemberCategory(), req.getCompanyId()); + Relation existRelation = searchRelation(null, req.getClearingMemberCategory(), req.getCompanyId()); boolean isClosedRelation = existRelation != null && IEnumKey.contains (existRelation.getServiceStatus(), Closed, ServiceStatus.Blocked, ServiceStatus.Suspended); if (existRelation != null && !isClosedRelation) { @@ -254,13 +254,14 @@ public class RelationService extends QueueConsumer implements InitializingBean { } } - private Relation searchRelation(String clearingMemberCategory, Long consumerId) { + private Relation searchRelation(ImdgTransaction tx, String clearingMemberCategory, Long consumerId) { String svc = serviceForCMC(clearingMemberCategory); if (svc == null) { log.warn("Unexpected relation clearingMemberCategory={}", clearingMemberCategory); return null; } - Relation existRelation = relationMap.getSingleObjectByFieldValues(Map.of( + Imdg currentRelationMap = tx == null ? relationMap : tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + Relation existRelation = currentRelationMap.getSingleObjectByFieldValues(Map.of( "consumerId", consumerId, "service", svc )); @@ -333,6 +334,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { ImdgTransaction tx = imdgProvider.newTransaction(); boolean txOk = false; try { + tx.beginTransaction(); relationUpdate0(tx, req, relation); // первоначальное ТЗ. boolean needAddNewRelation = false; // call relationNew0(req, companyId, firstCMC); @@ -501,6 +503,42 @@ public class RelationService extends QueueConsumer implements InitializingBean { Relation relation = relationMap.getSingleObjectByID(req.getId()); + Long companyId = relation.getConsumerId(); + + Collection companyCMC = selectCompanyClearingMemberCategory(companyId); + boolean companyCMCisBIV = companyCMC.contains(ClearingCategory.B.getKey()) || companyCMC.contains(ClearingCategory.I.getKey()) + || companyCMC.contains(ClearingCategory.V.getKey()); + boolean companyCMCisCF = companyCMC.contains(ClearingCategory.C.getKey()) || companyCMC.contains(ClearingCategory.F.getKey()); + String firstCMC = companyCMC.isEmpty() ? null : companyCMC.iterator().next(); + + if (companyCMCisBIV) { + // relation не изменяем + } else if (companyCMCisCF) { + // relation не изменяем + } + // else ? todo bad logic! Проверить ТЗ. + { + ImdgTransaction tx = imdgProvider.newTransaction(); + boolean txOk = false; + try { + tx.beginTransaction(); + if (!companyCMCisBIV) { + Relation relation1 = selectRelation(tx, companyId, MKR.getKey(), false); + if (relation1 != null) relationClose0(tx, relation1); + } + if (!companyCMCisCF) { + Relation relation1 = selectRelation(tx, companyId, FOND.getKey(), false); + if (relation1 != null) relationClose0(tx, relation1); + } + txOk = true; + } finally { + if (txOk) + tx.commitTransaction(); + else + tx.rollbackTransaction(); + } + } + if (WorkflowStatus.Blocked.equalsByKey(relation.getServiceStatus())) { log.warn("Relation {} already blocked.", relation.getId()); } else { @@ -530,7 +568,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { serviceStatus = serviceForCMC(clearingCategory).getKey(); } - Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId); + Relation relation = searchRelation(transaction, clearingMemberCategory.getClearingMemberCategory(), companyId); if (relation == null) { relation = new Relation(); relation.setId(idSequence.nextId()); @@ -561,7 +599,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { serviceStatus = serviceForCMC(clearingCategory).getKey(); } - Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId); + Relation relation = searchRelation(transaction, clearingMemberCategory.getClearingMemberCategory(), companyId); if (relation == null) { log.debug("Relation for clearingMemberCategory.id={} {} not found", clearingMemberCategory.getId(), clearingMemberCategory.getClearingMemberCategory()); return; @@ -574,6 +612,21 @@ public class RelationService extends QueueConsumer implements InitializingBean { relationMap.insert(relation); } + protected void cancelAllRelationForCompany(ImdgTransaction transaction, Long companyId) { + Objects.requireNonNull(companyId, "companyId"); + Imdg relationMap = transaction.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + Collection relations = relationMap.getCollectionObjectsByFieldValues(Map.of("consumerId", companyId)); + int cancelledN = 0; + for (Relation relation : relations) { + if (!Closed.equalsByKey(relation.getServiceStatus())) { + relation.setServiceStatus(Closed.getKey()); + relationMap.update(relation); + cancelledN++; + } + } + log.debug("Cancelled {} relation (of {}) by company {}", cancelledN, relations.size(), companyId); + } + protected void cancelingOfAgreementUpdateRelation(ImdgTransaction transaction, Long companyId) { Imdg relationMap = transaction.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); Relation relation = relationMap.getSingleObjectByFieldValues(Map.of("consumerId", companyId)); 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 a1e3a87fa..7101cbf0f 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 @@ -132,6 +132,7 @@ public interface Consts { String LIM_EXPORTED = "lim_exported"; String JOURNAL_SERVICE = "journal-service-exported"; String ACCOUNT_TERMINATION = "account-termination"; + String ACCOUNT_TERMINATION_STEP2 = "account-termination-step2"; String BALANCE_ACCOUNT_NEW = "balance-account-new"; String BALANCE_ACCOUNT_UPDATE = "balance-account-update"; String CONTINUE_CLEARING = "continue-clearing"; diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/account/sdf01/AccountTerminationRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/account/sdf01/AccountTerminationRequest.java index 75b351491..1b571f9f5 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/account/sdf01/AccountTerminationRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/account/sdf01/AccountTerminationRequest.java @@ -3,28 +3,10 @@ package ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01; import com.fasterxml.jackson.annotation.JsonProperty; public class AccountTerminationRequest { - @JsonProperty - private String account; - @JsonProperty - private Long accountId; @JsonProperty private Long companyId; - - public String getAccount() { - return account; - } - - public void setAccount(String account) { - this.account = account; - } - - public Long getAccountId() { - return accountId; - } - - public void setAccountId(Long accountId) { - this.accountId = accountId; - } + @JsonProperty + private Long terminationDocumentId; public Long getCompanyId() { return companyId; @@ -33,4 +15,12 @@ public class AccountTerminationRequest { public void setCompanyId(Long companyId) { this.companyId = companyId; } + + public Long getTerminationDocumentId() { + return terminationDocumentId; + } + + public void setTerminationDocumentId(Long terminationDocumentId) { + this.terminationDocumentId = terminationDocumentId; + } }