diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ClientCodeValidationConfig.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ClientCodeValidationConfig.java index 963e74fc1..61e03aeb8 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ClientCodeValidationConfig.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ClientCodeValidationConfig.java @@ -17,11 +17,15 @@ import ru.spcex.clearing.validation.common.rules.IdPresentRule; import ru.spcex.platform.classes.base.SpcexObjectBase; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.validation.IValidationRule; import ru.spcex.platform.utils.validation.IValidator; import ru.spcex.platform.utils.validation.ValidatorImpl; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; @@ -67,7 +71,9 @@ public class ClientCodeValidationConfig { acc -> Objects.equals(context.getValidatedObject().getCompanyId(), acc.getCompanyId()) ? null : AccountError.AccountNotFound ), - + new ExistAllCurrencyAccountId<>("currencyAccountList", + ClientCodeNewRequest::getCurrencyAccountList + ), DictionaryPresentRule.instance("stauts", ClientCodeNewRequest::getStatus, @@ -80,6 +86,41 @@ public class ClientCodeValidationConfig { }; } + public static class ExistAllCurrencyAccountId implements IValidationRule> { + String fieldName; + Function> idGetter; + + public ExistAllCurrencyAccountId(String fieldName, Function> idGetter) { + this.fieldName = fieldName; + this.idGetter = idGetter; + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + List ids = idGetter.apply(validatedObject); + if (ids == null || ids.isEmpty()) { + return empty(); // необязательное поле + } + Imdg imdg = context.obtainMap(IMDGDistributedNames.Map_Account, Account.class); + for (Long id:ids) { + if (id == null) { // null значения в массиве не ожидаются + return of(AccountError.RequiredFieldEmpty, fieldName); + } + Account byIdObject = imdg.getSingleObjectByID(id); + if (byIdObject == null) + return of(AccountError.AccountNotFound, id, fieldName); + } + + return empty(); + } + + @Override + public String ruleName() { + return getClass().getSimpleName() + "{" + fieldName + "}"; + } + } + @Bean("clientCodeUpdateRequestValidator") public Function clientCodeUpdateRequestValidator(Map> imdgForValidation) { return clientCodeUpdateRequest -> { @@ -128,6 +169,9 @@ public class ClientCodeValidationConfig { acc -> Objects.equals(context.getValidatedObject().getCompanyId(), acc.getCompanyId()) ? null : AccountError.AccountNotFound ), + new ExistAllCurrencyAccountId<>("currencyAccountList", + ClientCodeUpdateRequest::getCurrencyAccountList + ), DictionaryPresentRule.instance("stauts", ClientCodeUpdateRequest::getStatus, diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/TradingClearingRegistryListValidationConfig.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/TradingClearingRegistryListValidationConfig.java new file mode 100644 index 000000000..87344f73f --- /dev/null +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/TradingClearingRegistryListValidationConfig.java @@ -0,0 +1,169 @@ +package ru.spcex.clearing.account.config.validation; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistryList; +import ru.clearing.platform.dictionary.ServiceStatusDictionary; +import ru.spcex.clearing.account.errors.AccountError; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListUpdateRequest; +import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule; +import ru.spcex.clearing.validation.common.rules.IdPresentRule; +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.enumeration.AccountType; +import ru.spcex.platform.enumeration.WorkflowStatus; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.validation.IValidationRule; +import ru.spcex.platform.utils.validation.IValidator; +import ru.spcex.platform.utils.validation.ValidatorImpl; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Configuration +public class TradingClearingRegistryListValidationConfig { + + @Bean("tradingClearingRegistryListNewRequestValidator") + public Function tradingClearingRegistryListNewRequestValidator(Map> imdgForValidation) { + return tradingClearingRegistryListUpdateRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(tradingClearingRegistryListUpdateRequest); + Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); + addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistryList); + addImdg.accept(IMDGDistributedNames.Map_Account); + addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistry); + addImdg.accept(IMDGDistributedNames.Map_ServiceStatusDictionary); + return new ValidatorImpl<>(context, + IdPresentRule.instance("id", + TradingClearingRegistryListNewRequest::getTradingClearingRegistryId, + IMDGDistributedNames.Map_TradingClearingRegistry, + TradingClearingRegistry.class, + AccountError.RequiredFieldEmpty, + AccountError.TradingClearingRegistryNotFound, + false), + new ExistAllCurrencyAccountId<>("currencyAccountList", + TradingClearingRegistryListNewRequest::getCurrencyAccountList, + false, + null), + DictionaryPresentRule.instance("stauts", + TradingClearingRegistryListNewRequest::getStatus, + IMDGDistributedNames.Map_ServiceStatusDictionary, + ServiceStatusDictionary.class, + AccountError.RequiredFieldEmpty, + AccountError.DictionaryNotFound, + true) + ); + }; + } + + @Bean("tradingClearingRegistryListUpdateRequestValidator") + public Function tradingClearingRegistryListUpdateRequestValidator(Map> imdgForValidation) { + return tradingClearingRegistryListUpdateRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(tradingClearingRegistryListUpdateRequest); + Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); + addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistryList); + addImdg.accept(IMDGDistributedNames.Map_Account); + addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistry); + addImdg.accept(IMDGDistributedNames.Map_ServiceStatusDictionary); + return new ValidatorImpl>(context, + IdPresentRule.instance("id", + TradingClearingRegistryListUpdateRequest::getTradingClearingRegistryId, + IMDGDistributedNames.Map_TradingClearingRegistry, + TradingClearingRegistry.class, + AccountError.RequiredFieldEmpty, + AccountError.TradingClearingRegistryNotFound, + false), + new ExistAllCurrencyAccountId<>("currencyAccountList", + TradingClearingRegistryListUpdateRequest::getCurrencyAccountList, + false, + TradingClearingRegistryListUpdateRequest::getId + ), + DictionaryPresentRule.instance("stauts", + TradingClearingRegistryListUpdateRequest::getStatus, + IMDGDistributedNames.Map_ServiceStatusDictionary, + ServiceStatusDictionary.class, + AccountError.RequiredFieldEmpty, + AccountError.DictionaryNotFound, + true) + ); + }; + } + + + public static class ExistAllCurrencyAccountId implements IValidationRule> { + String fieldName; + Function> idListGetter; + boolean required; + Function idGetter; + + public ExistAllCurrencyAccountId(String fieldName, Function> idListGetter, boolean required, Function idGetter) { + this.fieldName = fieldName; + this.idListGetter = idListGetter; + this.required = required; + this.idGetter = idGetter; + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + List ids = idListGetter.apply(validatedObject); + if (ids == null || ids.isEmpty()) { + if (required) + return of(AccountError.RequiredFieldEmpty, fieldName); // обязательное поле + else + return Optional.empty(); + } + Imdg accountImdg = context.obtainMap(IMDGDistributedNames.Map_Account, Account.class); + // Проверить существование счетов + for (Long id : ids) { + if (id == null) { // null значения в массиве не ожидаются + return of(AccountError.RequiredFieldEmpty, fieldName); + } + Account byIdObject = accountImdg.getSingleObjectByID(id); + if (byIdObject == null) + return of(AccountError.AccountNotFound, id, fieldName); + if (!AccountType.Curr.equalsByKey(byIdObject.getAccountType())) { + return of(AccountError.AccountIsNotACurrency, id, fieldName); // Счет %S не валютный + } + } + + // Проверка отсутствия других TradingClearingRegistryList с этими счетами + Imdg tradingClearingRegistryListImdg = context.obtainMap(IMDGDistributedNames.Map_TradingClearingRegistryList, TradingClearingRegistryList.class); + ImdgPredicateBuilder pb = tradingClearingRegistryListImdg.predicateBuilder(); + ImdgPredicate query = pb.and(pb.equals("status", WorkflowStatus.Active.getKey()), + pb.in("accountId", ids.toArray(new Comparable[0]))); + if (idGetter != null) { + Long id = idGetter.apply(validatedObject); + if (id == null) // never + return of(AccountError.RequiredFieldEmpty, "id"); + query = pb.and(query, pb.not(pb.equals("id", id))); + } + Collection inOtherLists = tradingClearingRegistryListImdg.getCollectionObjectsByPredicate(query); + if (!inOtherLists.isEmpty()) { + Collection duplicateAccounts = inOtherLists.stream() + .map(TradingClearingRegistryList::getAccountId) + .filter(ids::contains) + .collect(Collectors.toSet()); + return of(AccountError.AccountForTradingClearingRegistryAlreadyUsed, duplicateAccounts, fieldName); // (5023) «Счет %s уже используется» + } + + return empty(); + } + + @Override + public String ruleName() { + return getClass().getSimpleName() + "{" + fieldName + "}"; + } + } + +} diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java index f170eef5d..6e6f29144 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java @@ -8,6 +8,7 @@ import ru.clearing.classes.statics.data.company.ClearingMemberCategory; import ru.clearing.classes.statics.data.company.Company; import ru.clearing.classes.statics.data.company.CompanySymbols; import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistryList; import ru.clearing.platform.dictionary.*; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.validation.common.ValidationHelper; @@ -42,6 +43,7 @@ public class ValidationConfig { addImdg.accept(IMDGDistributedNames.Map_ServiceStatusDictionary, ServiceStatusDictionary.class); addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); addImdg.accept(IMDGDistributedNames.Map_AccountSymbols, AccountSymbols.class); + addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistryList, TradingClearingRegistryList.class); //for ClientCodeValidationConfig addImdg.accept(IMDGDistributedNames.Map_ClientCode, ClientCode.class); diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/errors/AccountError.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/errors/AccountError.java index 584a60549..9e579812e 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/errors/AccountError.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/errors/AccountError.java @@ -23,6 +23,7 @@ public enum AccountError implements IErrorEnumId { AccountForTradingClearingRegistryAlreadyUsed(5023L), AccountFieldNotSet(5024L), AccountDepoTypeRequired(5025L), + AccountIsNotACurrency(5026L), // Счет %S не валютный TradingClearingRegistryNotFound(3022L), ; diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/ClientCodeService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/ClientCodeService.java index c3db9f826..226e41430 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/ClientCodeService.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/ClientCodeService.java @@ -9,6 +9,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Service; import ru.clearing.classes.statics.data.account.ClientCode; import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; @@ -20,6 +21,8 @@ import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeUpdateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListUpdateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryUpdateRequest; import ru.spcex.clearing.platform.messaging.serialization.LogFormatter; @@ -40,7 +43,9 @@ import ru.spcex.platform.utils.log.ExceptionUtils; import ru.spcex.platform.utils.validation.IValidator; import java.time.Instant; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -65,6 +70,8 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean private final RequestHelper requestHelper; protected TradingClearingRegistryService tradingClearingRegistryService; + protected ConfigurableApplicationContext context; + //protected TradingClearingRegistryListService tradingClearingRegistryListService; @Autowired public ClientCodeService(Consumer kafkaQueue, @@ -74,6 +81,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean IMessageResolver messageResolver, RequestHelper requestHelper, TradingClearingRegistryService tradingClearingRegistryService, + ConfigurableApplicationContext context, //TradingClearingRegistryListService tradingClearingRegistryListService, UserRoleVerification userRoleVerification, @Qualifier("clientCodeNewRequestValidator") Function clientCodeNewRequestValidator, @Qualifier("clientCodeUpdateRequestValidator") Function clientCodeUpdateRequestValidator, @@ -92,6 +100,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean this.messageResolver = messageResolver; this.userRoleVerification = userRoleVerification; this.tradingClearingRegistryService = tradingClearingRegistryService; + this.context = context; // TradingClearingRegistryListService } @Override @@ -119,10 +128,11 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean * Проверки * 3.5.1 если не была найдена запись в tradingClearingRegistry по companyId, moneyAccountId, depoAccountId и tradingClearingRegistryType=B (п 3.5.1) * 3.5.2 если не заполнены поля moneyAccountId ИЛИ moneyAccountId и depoAccountId + * * @param userRequest - * @param moneyAccountId req.getMoneyAccountId() - * @param depoAccountId req.getDepoAccountId() - * @param companyId req.getCompanyId() + * @param moneyAccountId req.getMoneyAccountId() + * @param depoAccountId req.getDepoAccountId() + * @param companyId req.getCompanyId() * @return */ RequestInfoUpdate crossValidate(BaseRequest userRequest, Long moneyAccountId, Long depoAccountId, Long companyId) { @@ -132,8 +142,8 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean error = new EnumMessage(AccountError.RequiredFieldEmpty, "companyId"); } else { TradingClearingRegistry tcr = tradingClearingRegistryMap.getFirstObjectByFieldValues(Map.of("companyId", companyId)); - if (tcr==null) { - error=new EnumMessage(AccountError.TradingClearingRegistryNotFound, companyId); + if (tcr == null) { + error = new EnumMessage(AccountError.TradingClearingRegistryNotFound, companyId); } } if (error != null) { @@ -144,6 +154,10 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean } protected RequestInfoUpdate clientCodeNew(BaseRequest userRequest) { + return clientCodeNew0(userRequest, false); + } + + protected RequestInfoUpdate clientCodeNew0(BaseRequest userRequest, boolean fromTCRList) { log.debug("ClientCodeNewRequest received {}", userRequest.getId()); RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); @@ -171,15 +185,57 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean } } - ClientCode newClientCode = buildClientCode(req); - clientCodeMap.insert(newClientCode); - log.debug("successfully processed, new clientCode id {}", newClientCode.getId()); + if (req.getCurrencyAccountList() == null || req.getCurrencyAccountList().isEmpty()) { + ClientCode newClientCode = buildClientCode(req); + clientCodeMap.insert(newClientCode); + log.debug("successfully processed, new clientCode id {}", newClientCode.getId()); + } else {//fixme transaction! + if (!fromTCRList) { + if (req.getMoneyAccountId() != null /*&& currencyAccountId != null*/) { + // Для каждого счета из списка (currencyAccountList) должна быть создана отдельная запись объекте clientCode с данным счетом для данного клиента + TradingClearingRegistry tcr = selectTradingClearingRegistry(req.getCompanyId(), req.getMoneyAccountId(), req.getDepoAccountId()); + + TradingClearingRegistryListNewRequest tcrListNewR = new TradingClearingRegistryListNewRequest(); + tcrListNewR.setTradingClearingRegistryId(tcr == null ? null : tcr.getId()); + tcrListNewR.setCurrencyAccountList(req.getCurrencyAccountList()); + tcrListNewR.setStatus(req.getStatus()); + + TradingClearingRegistryListService tradingClearingRegistryListService = context.getBean(TradingClearingRegistryListService.class); + + try { + BaseRequest request2 = new BaseRequest<>(); + request2.setId(idGenerator.nextId()); + request2.setActionType(ActionType.NEW); + request2.setRequestPayload(tcrListNewR); + requestInfoUpdate = tradingClearingRegistryListService.tradingClearingRegistryListNew0(request2, true); + if (requestInfoUpdate != null) return requestInfoUpdate; + } catch (Exception e) { + log.error("Can not wait creation of tradingClearingRegistryList. request id={}; {}", + userRequest.getId(), + ExceptionUtils.getStackTrace(e)); + return makeErrorResponse(userRequest, AccountError.GeneralError, "Can not create TCR: " + e.getMessage()); + } + } + } + List newClientCodeIds = new ArrayList<>(); + for (Long currencyAccountId : req.getCurrencyAccountList()) { + ClientCode newClientCode = buildClientCode(req); + newClientCode.setCurrencyAccountId(currencyAccountId); + clientCodeMap.insert(newClientCode); + newClientCodeIds.add(newClientCode.getId()); + log.trace("ClientCode.id={} for currencyAccountId={} created", newClientCode.getId(), currencyAccountId); + } + log.debug("successfully processed, new clientCode id {}", newClientCodeIds); + } return null; } - protected RequestInfoUpdate clientCodeNewFromApiUmCompany(BaseRequest userRequest) { + return clientCodeNewFromApiUmCompany0(userRequest, false); + } + + protected RequestInfoUpdate clientCodeNewFromApiUmCompany0(BaseRequest userRequest, boolean fromTCRList) { log.debug("ClientCodeNewRequest (UM_COMPANY) received {}", userRequest.getId()); RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); @@ -208,9 +264,47 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean } } - ClientCode newClientCode = buildClientCode(req); - clientCodeMap.insert(newClientCode); - log.debug("successfully processed, new clientCode id {}", newClientCode.getId()); + if (req.getCurrencyAccountList() == null || req.getCurrencyAccountList().isEmpty()) { + ClientCode newClientCode = buildClientCode(req); + clientCodeMap.insert(newClientCode); + log.debug("successfully processed, new clientCode id {}", newClientCode.getId()); + } else {//fixme transaction! + if (!fromTCRList) { + if (req.getMoneyAccountId() != null /*&& currencyAccountId != null*/) { + // Для каждого счета из списка (currencyAccountList) должна быть создана отдельная запись объекте clientCode с данным счетом для данного клиента + TradingClearingRegistry tcr = selectTradingClearingRegistry(req.getCompanyId(), req.getMoneyAccountId(), req.getDepoAccountId()); + + TradingClearingRegistryListUpdateRequest tcrListUpdateR = new TradingClearingRegistryListUpdateRequest(); + tcrListUpdateR.setTradingClearingRegistryId(tcr == null ? null : tcr.getId()); + tcrListUpdateR.setCurrencyAccountList(req.getCurrencyAccountList()); + + TradingClearingRegistryListService tradingClearingRegistryListService = context.getBean(TradingClearingRegistryListService.class); + + try { + BaseRequest request2 = new BaseRequest<>(); + request2.setId(idGenerator.nextId()); + request2.setActionType(ActionType.NEW); + request2.setRequestPayload(tcrListUpdateR); + requestInfoUpdate = tradingClearingRegistryListService.tradingClearingRegistryListUpdate0(request2, true); + if (requestInfoUpdate != null) return requestInfoUpdate; + } catch (Exception e) { + log.error("Can not wait creation of tradingClearingRegistryList. request id={}; {}", + userRequest.getId(), + ExceptionUtils.getStackTrace(e)); + return makeErrorResponse(userRequest, AccountError.GeneralError, "Can not create TCR: " + e.getMessage()); + } + } + } + List newClientCodeIds = new ArrayList<>(); + for (Long currencyAccountId : req.getCurrencyAccountList()) { + ClientCode newClientCode = buildClientCode(req); + newClientCode.setCurrencyAccountId(currencyAccountId); + clientCodeMap.insert(newClientCode); + newClientCodeIds.add(newClientCode.getId()); + log.trace("ClientCode.id={} for currencyAccountId={} created", newClientCode.getId(), currencyAccountId); + } + log.debug("successfully processed, new clientCode id {}", newClientCodeIds); + } return null; } @@ -250,6 +344,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean } updateClientCode(clientCode, req); + //fixme спросить, у нас при апдейте передаётся clientCode.id которого надо изменять. А тут список currencyAccountList приходит - в нём что будет - только 1 счёт для него и его же менять? clientCodeMap.update(clientCode); log.debug("successfully processed update, id {}", clientCode.getId()); @@ -283,7 +378,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean return null; } - TradingClearingRegistry selectTradingClearingRegistry(Long companyId, Long moneyAccountId, Long depoAccountId) { + protected TradingClearingRegistry selectTradingClearingRegistry(Long companyId, Long moneyAccountId, Long depoAccountId) { Map> query = new HashMap<>(); query.put("companyId", companyId); query.put("moneyAccountId", moneyAccountId); @@ -310,7 +405,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean } protected RequestInfoUpdate createAndWaitTCR(Long reqId, Long clientCode, - Long companyId, Long moneyAccountId, Long depoAccountId) { + Long companyId, Long moneyAccountId, Long depoAccountId) { log.debug("For request {}, clientCode={} need create TCR: companyId={}, moneyAccountId={}, depoAccountId={}", reqId, clientCode == null ? "new" : clientCode, companyId, moneyAccountId, depoAccountId); diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/TradingClearingRegistryListService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/TradingClearingRegistryListService.java new file mode 100644 index 000000000..970bda1a6 --- /dev/null +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/TradingClearingRegistryListService.java @@ -0,0 +1,240 @@ +package ru.spcex.clearing.account.service; + +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.producer.Producer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.account.ClearingAccount; +import ru.clearing.classes.statics.data.account.DepoAccount; +import ru.clearing.classes.statics.data.account.InformationAccount; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.company.relation.Relation; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistryList; +import ru.spcex.clearing.account.errors.AccountError; +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.ClientCodeNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.clearing.CreateRegistryByTCRListRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.clearing.CreateRegistryRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListUpdateRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryUpdateRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.reports.NotificationRequest; +import ru.spcex.clearing.platform.messaging.serialization.LogFormatter; +import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; +import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.clearing.util.services.RequestHelper; +import ru.spcex.clearing.validation.common.ValidationHelper; +import ru.spcex.platform.enumeration.ServiceStatus; +import ru.spcex.platform.enumeration.TradingClearingRegistryPurpose; +import ru.spcex.platform.enumeration.TradingClearingRegistryType; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.utils.enumeration.IMessageResolver; +import ru.spcex.platform.utils.log.ExceptionUtils; +import ru.spcex.platform.utils.validation.IValidator; + +import java.time.Instant; +import java.util.*; +import java.util.function.Function; + +@Service +public class TradingClearingRegistryListService extends QueueConsumer implements InitializingBean { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ValidationHelper validationHelper; + private final UserRoleVerification userRoleVerification; + protected ClientCodeService clientCodeService; + + private final ImdgProvider imdgProvider; + private final Imdg tradingClearingRegistryListImdg; + private final Imdg tradingClearingRegistryImdg; + private final Imdg depoAccountImdg; + private final Imdg clearingAccountImdg; + private final Imdg informationAccountImdg; + private final Imdg accountImdg; + private final Imdg companyImdg; + private final Imdg relationImdg; + + private final Function tradingClearingRegistryListUpdateRequestValidator; + private final RequestHelper requestHelper; + private final Function tradingClearingRegistryListNewRequestValidator; +// private final Function tradingClearingRegistryListBlockRequestValidator; + + private final IMessageResolver messageResolver; + private final Producer kafkaProducer; + private final KafkaSender kafkaSender; + + public TradingClearingRegistryListService(Consumer kafkaQueue, + Producer kafkaProducer, + KafkaSender kafkaSender, + ImdgProvider imdgProvider, + ClientCodeService clientCodeService, + ValidationHelper validationHelper, + UserRoleVerification userRoleVerification, + IMessageResolver messageResolver, + RequestHelper requestHelper, + @Qualifier("tradingClearingRegistryListNewRequestValidator") + Function tradingClearingRegistryListNewRequestValidator, + @Qualifier("tradingClearingRegistryListUpdateRequestValidator") + Function tradingClearingRegistryListUpdateRequestValidator + ) { + super(kafkaQueue, kafkaProducer); + this.kafkaProducer = kafkaProducer; + this.kafkaSender = kafkaSender; + this.validationHelper = validationHelper; + this.userRoleVerification = userRoleVerification; + this.requestHelper = requestHelper; + this.imdgProvider = imdgProvider; + this.tradingClearingRegistryListImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistryList, TradingClearingRegistryList.class); + this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); + this.depoAccountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_DepoAccount, DepoAccount.class); + this.clearingAccountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingAccount, ClearingAccount.class); + this.informationAccountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_InformationAccount, InformationAccount.class); + this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + this.accountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class); + this.relationImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + this.tradingClearingRegistryListNewRequestValidator = tradingClearingRegistryListNewRequestValidator; + this.tradingClearingRegistryListUpdateRequestValidator = tradingClearingRegistryListUpdateRequestValidator; +// this.tradingClearingRegistryListBlockRequestValidator = tradingClearingRegistryListBlockRequestValidator; + this.messageResolver = messageResolver; + this.clientCodeService = clientCodeService; + } + + @Override + public void afterPropertiesSet() throws Exception { + imdgProvider.waitAvailable(); + callback(TradingClearingRegistryListNewRequest.class) + .setFunction(this::tradingClearingRegistryListNew) + .forDestination(Consts.DESTINATION_TRADING_CLEARING_REGISTRIES_LIST_NEW, callbacks::put); + callback(TradingClearingRegistryListUpdateRequest.class) + .setFunction(this::tradingClearingRegistryListUpdate) + .forDestination(Consts.DESTINATION_TRADING_CLEARING_REGISTRIES_LIST_UPDATE, callbacks::put); + init(); + } + + public RequestInfoUpdate tradingClearingRegistryListNew(BaseRequest userRequest) { + return tradingClearingRegistryListNew0(userRequest, false); + } + + protected RequestInfoUpdate tradingClearingRegistryListNew0(BaseRequest userRequest, boolean innerCall) { + log.debug("TradingClearingRegistryListNewRequest received"); + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); + if (requestInfoUpdate != null) { + return requestInfoUpdate; + } + + requestInfoUpdate = validationHelper.validateTillFirstError(userRequest, tradingClearingRegistryListNewRequestValidator); + if (requestInfoUpdate != null) { + return requestInfoUpdate; + } + + TradingClearingRegistryListNewRequest req = userRequest.getRequestPayload(); + Instant now = Instant.now(); + + List newIds=new ArrayList<>(); + for (Long currAccId: req.getCurrencyAccountList()) { + Long id = tradingClearingRegistryListImdg.nextIDSequenceFor(); + TradingClearingRegistryList tradingClearingRegistryList = new TradingClearingRegistryList(); + tradingClearingRegistryList.setId(id); + tradingClearingRegistryList.setCreated(now); + tradingClearingRegistryList.setUpdated(now); + tradingClearingRegistryList.setTradingClearingRegistryId(req.getTradingClearingRegistryId()); + tradingClearingRegistryList.setAccountId(currAccId); + if (req.getStatus() == null) { + tradingClearingRegistryList.setStatus(ServiceStatus.Active.getKey()); + log.trace("TCRList status in request not set. Use default: {}", tradingClearingRegistryList.getStatus()); + } else { + tradingClearingRegistryList.setStatus(req.getStatus()); + } + tradingClearingRegistryListImdg.insert(tradingClearingRegistryList); + newIds.add(id); + + } + log.info("New TCRList.id={} has created.", newIds); + + if (!innerCall) { + // Для каждого счета из списка (currencyAccountList) должна быть создана отдельная запись объекте clientCode с данным счетом для данного клиента, т.е. у одного клиента может быть несколько валютных счетов. + try { + TradingClearingRegistry tcr = tradingClearingRegistryImdg.getSingleObjectByID(req.getTradingClearingRegistryId()); + ClientCodeNewRequest cCodeReq = new ClientCodeNewRequest(); + cCodeReq.setTradingClearingRegistryId(req.getTradingClearingRegistryId()); + cCodeReq.setDepoAccountId(tcr.getDepoAccountId()); + cCodeReq.setMoneyAccountId(tcr.getMoneyAccountId()); + cCodeReq.setCompanyId(tcr.getCompanyId()); + cCodeReq.setCurrencyAccountList(req.getCurrencyAccountList()); + cCodeReq.setStatus(req.getStatus()); + //todo cCodeReq.setCode(); + BaseRequest request2 = new BaseRequest<>(); + request2.setRequestPayload(cCodeReq); + requestInfoUpdate = clientCodeService.clientCodeNew0(request2, true); + if (requestInfoUpdate != null) return requestInfoUpdate; + } catch (Exception e) { + log.error("Can not wait creation of tradingClearingRegistryList. request id={}; {}", + userRequest.getId(), + ExceptionUtils.getStackTrace(e)); + return requestHelper.makeErrorResponse(userRequest, AccountError.GeneralError, "Can not create TCR: " + e.getMessage()); + } + + } + + sendNotificationToClearingSvc(newIds); + + log.debug("successfully processed, id {}", newIds); + return null; + } + + public RequestInfoUpdate tradingClearingRegistryListUpdate(BaseRequest userRequest) { + return tradingClearingRegistryListUpdate0(userRequest, false); + } + + public RequestInfoUpdate tradingClearingRegistryListUpdate0(BaseRequest userRequest, boolean innerCall) { + log.debug("TradingClearingRegistryListUpdateRequest received"); + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); + if (requestInfoUpdate != null) return requestInfoUpdate; + + requestInfoUpdate = validationHelper.validateTillFirstError(userRequest, tradingClearingRegistryListUpdateRequestValidator); + if (requestInfoUpdate != null) return requestInfoUpdate; + + TradingClearingRegistryListUpdateRequest req = userRequest.getRequestPayload(); + TradingClearingRegistryList tradingClearingRegistryList = tradingClearingRegistryListImdg.getSingleObjectByID(req.getId()); + + //todo какие поля нужно обновлять? + if (req.getStatus() != null && !Objects.equals(req.getStatus(), tradingClearingRegistryList.getStatus())) { + Instant now = Instant.now(); + tradingClearingRegistryList.setUpdated(now); + tradingClearingRegistryList.setStatus(req.getStatus()); + tradingClearingRegistryListImdg.update(tradingClearingRegistryList); + log.debug("Update TCRList.id={}.", tradingClearingRegistryList.getId()); + } else { + log.debug("Nothing to update TCRList.id={}.", tradingClearingRegistryList.getId()); + } + + log.debug("successfully processed, id {}", tradingClearingRegistryList.getId()); + return null; + } + + + + /** + * clearing-service сообщение на открытие клиринговых регистров; + */ + protected void sendNotificationToClearingSvc(List tradingClearingRegistries) { + CreateRegistryByTCRListRequest request = new CreateRegistryByTCRListRequest(); + request.setTradingClearingRegistryListIds(tradingClearingRegistries); + log.debug("Send message to kafka \"{}\": {}", Consts.DESTINATION_TRADING_CLEARING_REGISTRY_ON_TCRLIST_NEW, LogFormatter.toStringWrapper(request)); + kafkaSender.sendRequestToQueue(Consts.DESTINATION_TRADING_CLEARING_REGISTRY_ON_TCRLIST_NEW, request); + } + + +} diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/ClientCodeServiceTest.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/ClientCodeServiceTest.java index 54425683e..ff4ff204e 100644 --- a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/ClientCodeServiceTest.java +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/ClientCodeServiceTest.java @@ -19,9 +19,11 @@ import ru.clearing.classes.statics.data.account.DepoAccount; import ru.clearing.classes.statics.data.company.Company; import ru.clearing.classes.statics.data.profile.CompanyInfo; import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistryList; import ru.clearing.platform.dictionary.*; import ru.spcex.clearing.account.config.BeanConfiguration; import ru.spcex.clearing.account.config.validation.ClientCodeValidationConfig; +import ru.spcex.clearing.account.config.validation.TradingClearingRegistryListValidationConfig; import ru.spcex.clearing.account.config.validation.TradingClearingRegistryValidationConfig; import ru.spcex.clearing.account.config.validation.ValidationConfig; import ru.spcex.clearing.imdg.IMDGDistributedNames; @@ -35,6 +37,8 @@ import ru.spcex.clearing.test.TestObjectCreator; import ru.spcex.clearing.test.TestUtils; import ru.spcex.clearing.test.config.ImdgTestConfig; import ru.spcex.clearing.test.config.KafkaTestConfig; +import ru.spcex.platform.enumeration.AccountType; +import ru.spcex.platform.enumeration.ServiceStatus; import ru.spcex.platform.enumeration.TradingClearingRegistryType; import ru.spcex.platform.enumeration.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; @@ -42,6 +46,8 @@ import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; import javax.annotation.PostConstruct; +import java.util.Arrays; + import static org.junit.jupiter.api.Assertions.*; import static ru.spcex.clearing.test.TestUtils.waitingSendAndCheckRecord; @@ -53,6 +59,9 @@ import static ru.spcex.clearing.test.TestUtils.waitingSendAndCheckRecord; TradingClearingRegistryService.class, TradingClearingRegistryValidationConfig.class, + TradingClearingRegistryListService.class, + TradingClearingRegistryListValidationConfig.class, + ValidationConfig.class, BeanConfiguration.class, @@ -79,6 +88,7 @@ class ClientCodeServiceTest { protected Producer mockProducer; private Imdg clientCodeImdg; + private Imdg tradingClearingRegistryListImdg; // ****************************-******************* @@ -87,9 +97,11 @@ class ClientCodeServiceTest { private void init() { hazelcastServiceTest.waitAvailable(); clientCodeImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_ClientCode, ClientCode.class); + tradingClearingRegistryListImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_TradingClearingRegistryList, TradingClearingRegistryList.class); // Словари для теста, применяются в ValidationConfig putToDictionary(IMDGDistributedNames.Map_WorkflowStatusDictionary, new WorkflowStatusDictionary(), "ACTV"); + putToDictionary(IMDGDistributedNames.Map_ServiceStatusDictionary, new ServiceStatusDictionary(), ServiceStatus.Active.getKey()); putToDictionary(IMDGDistributedNames.Map_CompanySymbolDictionary, new CompanySymbolDictionary(), "CLRC"); putToDictionary(IMDGDistributedNames.Map_CorporationSoleTypeDictionary, new CorporationSoleTypeDictionary(), "GDIR"); putToDictionary(IMDGDistributedNames.Map_CountryCodeDictionary, new CountryCodeDictionary(), "RUS"); @@ -149,6 +161,14 @@ class ClientCodeServiceTest { depoAcc.setAccountId(depoAccount.getId()); depoAccounts.insert(depoAcc); + Account c2Account = new Account(); + c2Account.setId(133L); + c2Account.setAccount("AAAX-44654-CURR"); + c2Account.setStatus("ACTV"); + c2Account.setCompanyId(COMPANY_ID); // для валидации принадлежности счёта + c2Account.setAccountType(AccountType.Curr.getKey()); + accounts.insert(c2Account); + new TestObjectCreator(hazelcastServiceTest).createUserAdmin(1000L); } @@ -207,7 +227,7 @@ class ClientCodeServiceTest { TestUtils.addRecordToKafka((MockConsumer) clientCodeService.getConsumer(), Consts.DESTINATION_CLIENT_CODE_NEW, PARTITION, 0, jsonString); //ASSERT - waitingSendAndCheckRecord(ID, mockProducer); + waitingSendAndCheckRecord(ID, mockProducer); ClientCode resultNew = clientCodeImdg.getFirstObjectBySQL(String.format("code = '%s'", ccCode)); predictableClientCode.setId(resultNew.getId()); CLIENT_CODE_MATCHER.assertMatch(resultNew, predictableClientCode); @@ -292,6 +312,52 @@ class ClientCodeServiceTest { assertNotNull(resultNew.getCreated()); } + + /** + * {@link ClientCodeService#clientCodeUpdate(BaseRequest)}
+ * Тест проверяет создание {@link ClientCode} в IMDG при передаче из Apache Kafka (очередь 4).
+ * Входной запрос {@link ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeNewRequest}:
+ * С дополнительным заполнением TradingClearingRegistryList id's. С одним. + **/ + @Test + void clientCodeNew4() { + //ARRANGE + final String ccCode = "Lucky"; + ClientCodeNewRequest clientCodeNewRequest = new ClientCodeNewRequest(); + clientCodeNewRequest.setCompanyId(COMPANY_ID); + clientCodeNewRequest.setCode(ccCode); + clientCodeNewRequest.setTradingClearingRegistryId(TCR_ID); + clientCodeNewRequest.setMoneyAccountId(131L); + clientCodeNewRequest.setDepoAccountId(132L); + clientCodeNewRequest.setCurrencyAccountList(Arrays.asList(133L)); + clientCodeNewRequest.setStatus("ACTV"); + + ClientCode predictableClientCode = new ClientCode(); + predictableClientCode.setCode(ccCode); + predictableClientCode.setStatus("ACTV"); + predictableClientCode.setCompanyId(COMPANY_ID); + predictableClientCode.setMoneyAccountId(131L); + predictableClientCode.setDepoAccountId(132L); + predictableClientCode.setCurrencyAccountId(133L); + predictableClientCode.setTradingClearingRegistryId(TCR_ID); + + //ACT + String jsonString = TestUtils.getJsonStringForNew(clientCodeNewRequest, ID); + + TestUtils.addRecordToKafka((MockConsumer) clientCodeService.getConsumer(), Consts.DESTINATION_CLIENT_CODE_NEW, PARTITION, 0, jsonString); + + //ASSERT + waitingSendAndCheckRecord(ID, mockProducer); + ClientCode resultNew = clientCodeImdg.getFirstObjectBySQL(String.format("code = '%s'", ccCode)); + predictableClientCode.setId(resultNew.getId()); + CLIENT_CODE_MATCHER.assertMatch(resultNew, predictableClientCode); + assertNotNull(resultNew.getCreated()); + + TradingClearingRegistryList newTCRList = tradingClearingRegistryListImdg.getFirstObjectBySQL("accountId=" + predictableClientCode.getCurrencyAccountId()); + assertNotNull(newTCRList); + assertEquals(TCR_ID, newTCRList.getTradingClearingRegistryId()); + } + /** * {@link ClientCodeService#clientCodeUpdate(BaseRequest)}
* Тест проверяет обновление сущности {@link ClientCode} в IMDG при передаче из Apache Kafka.
diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/TradingClearingRegistryListServiceTest.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/TradingClearingRegistryListServiceTest.java new file mode 100644 index 000000000..f09dd40f7 --- /dev/null +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/TradingClearingRegistryListServiceTest.java @@ -0,0 +1,282 @@ +package ru.spcex.clearing.account.service; + +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.account.ClearingAccount; +import ru.clearing.classes.statics.data.account.ClientCode; +import ru.clearing.classes.statics.data.account.DepoAccount; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.profile.CompanyInfo; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistryList; +import ru.clearing.platform.dictionary.*; +import ru.spcex.clearing.account.config.BeanConfiguration; +import ru.spcex.clearing.account.config.validation.ClientCodeValidationConfig; +import ru.spcex.clearing.account.config.validation.TradingClearingRegistryListValidationConfig; +import ru.spcex.clearing.account.config.validation.TradingClearingRegistryValidationConfig; +import ru.spcex.clearing.account.config.validation.ValidationConfig; +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.ClientCodeNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeUpdateRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListUpdateRequest; +import ru.spcex.clearing.test.MatcherFactory; +import ru.spcex.clearing.test.TestObjectCreator; +import ru.spcex.clearing.test.TestUtils; +import ru.spcex.clearing.test.config.ImdgTestConfig; +import ru.spcex.clearing.test.config.KafkaTestConfig; +import ru.spcex.platform.enumeration.AccountType; +import ru.spcex.platform.enumeration.ServiceStatus; +import ru.spcex.platform.enumeration.TradingClearingRegistryType; +import ru.spcex.platform.enumeration.WorkflowStatus; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; + +import javax.annotation.PostConstruct; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.*; +import static ru.spcex.clearing.test.TestUtils.waitingSendAndCheckRecord; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + TradingClearingRegistryListService.class, + TradingClearingRegistryListValidationConfig.class, + ClientCodeService.class, + ClientCodeValidationConfig.class, + + TradingClearingRegistryService.class, + TradingClearingRegistryValidationConfig.class, + + ValidationConfig.class, + BeanConfiguration.class, + + KafkaTestConfig.class, + ImdgTestConfig.class}) +class TradingClearingRegistryListServiceTest { + private static final int PARTITION = 0; + private static final Long ID = 4L; + public static final MatcherFactory.Matcher CLIENT_CODE_MATCHER = MatcherFactory.usingIgnoringFieldsComparator("created", "updated"); + public static final MatcherFactory.Matcher TRADING_CLEARING_REGISTRY_LIST_MATCHER = MatcherFactory.usingIgnoringFieldsComparator("created", "updated"); + + private static final Long TCR_ID = 41L; + private static final Long COMPANY_ID = 42L; + + @Autowired + TradingClearingRegistryListService tradingClearingRegistryListService; + @Autowired + @Qualifier("hazelcastServiceTest") + private HazelcastService hazelcastServiceTest; + @Captor + private ArgumentCaptor producerRecord; + @Autowired + @Qualifier("mockProducer") + protected Producer mockProducer; + + private Imdg clientCodeImdg; + private Imdg tradingClearingRegistryListImdg; + + + // ****************************-******************* + + @PostConstruct + private void init() { + hazelcastServiceTest.waitAvailable(); + clientCodeImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_ClientCode, ClientCode.class); + tradingClearingRegistryListImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_TradingClearingRegistryList, TradingClearingRegistryList.class); + + // Словари для теста, применяются в ValidationConfig + putToDictionary(IMDGDistributedNames.Map_WorkflowStatusDictionary, new WorkflowStatusDictionary(), "ACTV"); + putToDictionary(IMDGDistributedNames.Map_ServiceStatusDictionary, new ServiceStatusDictionary(), ServiceStatus.Active.getKey()); + putToDictionary(IMDGDistributedNames.Map_CompanySymbolDictionary, new CompanySymbolDictionary(), "CLRC"); + putToDictionary(IMDGDistributedNames.Map_CorporationSoleTypeDictionary, new CorporationSoleTypeDictionary(), "GDIR"); + putToDictionary(IMDGDistributedNames.Map_CountryCodeDictionary, new CountryCodeDictionary(), "RUS"); + putToDictionary(IMDGDistributedNames.Map_AllowedDictionary, new AllowedDictionary(), "ALWD"); + putToDictionary(IMDGDistributedNames.Map_LegalKindDictionary, new LegalKindDictionary(), "JURD"); + putToDictionary(IMDGDistributedNames.Map_OrganizationTypeDictionary, new OrganizationTypeDictionary(), "NCRD"); + + + Imdg companyImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Company, Company.class); + Company company1 = new Company(); + company1.setId(COMPANY_ID); + company1.setWorkflowStatus(WorkflowStatus.Active.getKey()); + company1.setFullName("Company prime"); + company1.setShortName("Seizwell"); + company1.setProfile(new CompanyInfo()); + company1.getProfile().setCompanyId(COMPANY_ID); + company1.getProfile().setCountryCode("TLDI"); + company1.getProfile().setDescription("Big profit from TLD Company Prime."); + company1.getProfile().setLegalKind("TLDI"); + company1.getProfile().setResidence("TLDI"); + companyImdg.insert(company1); + + Imdg tcrImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); + TradingClearingRegistry registry1 = new TradingClearingRegistry(); + registry1.setId(TCR_ID); + registry1.setCompanyId(COMPANY_ID); + registry1.setCode("code-120-101"); + registry1.setMoneyAccountId(131L); + registry1.setDepoAccountId(132L); + registry1.setTradingClearingRegistryType(TradingClearingRegistryType.Client_B.getKey()); + registry1.setStatus(WorkflowStatus.Active.getKey()); + tcrImdg.insert(registry1); + + Imdg accounts = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Account, Account.class); + Imdg clsAccounts = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_ClearingAccount, ClearingAccount.class); + Account moneyAccount = new Account(); + moneyAccount.setId(131L); + moneyAccount.setAccount("AAAA-4444"); + moneyAccount.setStatus("ACTV"); + moneyAccount.setCompanyId(COMPANY_ID); // для валидации принадлежности счёта + accounts.insert(moneyAccount); + ClearingAccount clsAcc = new ClearingAccount(); + clsAcc.setId(moneyAccount.getId()); + clsAcc.setCompanyId(COMPANY_ID); + clsAcc.setAccountId(moneyAccount.getId()); + clsAccounts.insert(clsAcc); + Imdg depoAccounts = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_DepoAccount, DepoAccount.class); + Account depoAccount = new Account(); + depoAccount.setId(132L); + depoAccount.setAccount("AAAB-44654"); + depoAccount.setStatus("ACTV"); + depoAccount.setCompanyId(COMPANY_ID); // для валидации принадлежности счёта + accounts.insert(depoAccount); + DepoAccount depoAcc = new DepoAccount(); + depoAcc.setId(depoAccount.getId()); + depoAcc.setCompanyId(COMPANY_ID); + depoAcc.setAccountId(depoAccount.getId()); + depoAccounts.insert(depoAcc); + + Account c2Account = new Account(); + c2Account.setId(133L); + c2Account.setAccount("AAAX-44654-CURR"); + c2Account.setStatus("ACTV"); + c2Account.setCompanyId(COMPANY_ID); // для валидации принадлежности счёта + c2Account.setAccountType(AccountType.Curr.getKey()); + accounts.insert(c2Account); + + Account c3Account = new Account(); + c3Account.setId(134L); + c3Account.setAccount("AAAX-12654-CURR"); + c3Account.setStatus("ACTV"); + c3Account.setCompanyId(COMPANY_ID); // для валидации принадлежности счёта + c3Account.setAccountType(AccountType.Curr.getKey()); + accounts.insert(c3Account); + + new TestObjectCreator(hazelcastServiceTest).createUserAdmin(1000L); + } + + private void putToDictionary(String mapName, D object, String code) { + Imdg dMap = (Imdg) hazelcastServiceTest.getImdg(mapName, object.getClass()); + object.setId(2L); + object.setCode(code); + object.setName("name of " + code); + dMap.insert(object); + } + + + /** + * {@link TradingClearingRegistryListService#tradingClearingRegistryListNew(BaseRequest)}
+ * Тест проверяет создание {@link TradingClearingRegistryList} в IMDG при передаче из Apache Kafka (очередь 1).
+ * Входной запрос {@link TradingClearingRegistryListNewRequest}:
+ **/ + @Test + void tradingClearingRegistryListNew() { + //ARRANGE + final String ccCode = null; + TradingClearingRegistryListNewRequest tcrlNewRequest = new TradingClearingRegistryListNewRequest(); + tcrlNewRequest.setTradingClearingRegistryId(TCR_ID); + tcrlNewRequest.setCurrencyAccountList(Arrays.asList(134L)); + tcrlNewRequest.setStatus("ACTV"); + + ClientCode predictableClientCode = new ClientCode(); + predictableClientCode.setCode(ccCode); + predictableClientCode.setStatus("ACTV"); + predictableClientCode.setCompanyId(COMPANY_ID); + + TradingClearingRegistryList predictableTradingClearingRegistryList = new TradingClearingRegistryList(); + predictableTradingClearingRegistryList.setAccountId(134L); + predictableTradingClearingRegistryList.setStatus("ACTV"); + predictableTradingClearingRegistryList.setCurrency("RUB"); + + //ACT + String jsonString = TestUtils.getJsonStringForNew(tcrlNewRequest, ID); + + TestUtils.addRecordToKafka((MockConsumer) tradingClearingRegistryListService.getConsumer(), Consts.DESTINATION_TRADING_CLEARING_REGISTRIES_LIST_NEW, PARTITION, 0, jsonString); + + //ASSERT + waitingSendAndCheckRecord(ID, mockProducer); + ClientCode resultCCNew = clientCodeImdg.getFirstObjectBySQL(String.format("code = '%s'", ccCode)); + predictableClientCode.setId(resultCCNew.getId()); + CLIENT_CODE_MATCHER.assertMatch(resultCCNew, predictableClientCode); + assertNotNull(resultCCNew.getCreated()); + + TradingClearingRegistryList resultTCRLNew = tradingClearingRegistryListImdg.getFirstObjectBySQL(String.format("code = '%s'", ccCode)); + predictableClientCode.setId(resultTCRLNew.getId()); + TRADING_CLEARING_REGISTRY_LIST_MATCHER.assertMatch(resultTCRLNew, predictableTradingClearingRegistryList); + assertNotNull(resultTCRLNew.getCreated()); + + // CLEAN UP + tradingClearingRegistryListImdg.delete(resultTCRLNew); + } + + /** + * {@link TradingClearingRegistryListService#tradingClearingRegistryListUpdate(BaseRequest)}
+ * Тест проверяет обновление сущности {@link TradingClearingRegistryList} в IMDG при передаче из Apache Kafka.
+ * Входной запрос {@link TradingClearingRegistryListUpdateRequest}:
+ **/ + @Test + void tradingClearingRegistryListUpdate() { + //ARRANGE + TradingClearingRegistryList existsTradingClearingRegistryList = new TradingClearingRegistryList(); + existsTradingClearingRegistryList.setId(ID); + existsTradingClearingRegistryList.setTradingClearingRegistryId(TCR_ID); + existsTradingClearingRegistryList.setCurrency("BTC"); + existsTradingClearingRegistryList.setAccountId(133L); + tradingClearingRegistryListImdg.insert(existsTradingClearingRegistryList); + + TradingClearingRegistryListUpdateRequest tradingClearingRegistryListUpdateRequest = new TradingClearingRegistryListUpdateRequest(); + tradingClearingRegistryListUpdateRequest.setId(ID); + tradingClearingRegistryListUpdateRequest.setTradingClearingRegistryId(TCR_ID); + tradingClearingRegistryListUpdateRequest.setStatus("ACTV"); + tradingClearingRegistryListUpdateRequest.setCurrencyAccountList(Arrays.asList(134L)); + + TradingClearingRegistryList predictableTradingClearingRegistryList = new TradingClearingRegistryList(); + predictableTradingClearingRegistryList.setId(ID); + predictableTradingClearingRegistryList.setTradingClearingRegistryId(TCR_ID); + predictableTradingClearingRegistryList.setCurrency("BTC"); + predictableTradingClearingRegistryList.setAccountId(133L); + predictableTradingClearingRegistryList.setStatus("ACTV"); + + //ACT + String jsonString = TestUtils.getJsonStringForUpdate(tradingClearingRegistryListUpdateRequest, ID); + + TestUtils.addRecordToKafka((MockConsumer) tradingClearingRegistryListService.getConsumer(), Consts.DESTINATION_TRADING_CLEARING_REGISTRIES_LIST_UPDATE, PARTITION, 0, jsonString); + + //ASSERT + waitingSendAndCheckRecord(ID, mockProducer); + + TradingClearingRegistryList resultUpdating = tradingClearingRegistryListImdg.getSingleObjectByID(ID); + TRADING_CLEARING_REGISTRY_LIST_MATCHER.assertMatch(resultUpdating, predictableTradingClearingRegistryList); + assertNotNull(resultUpdating.getUpdated()); + + // CLEAN UP + tradingClearingRegistryListImdg.delete(resultUpdating); + } + +} \ No newline at end of file diff --git a/clearing-parent/backend-api/src/main/resources/meta/data.xml b/clearing-parent/backend-api/src/main/resources/meta/data.xml index 2202cd120..8bccf803a 100644 --- a/clearing-parent/backend-api/src/main/resources/meta/data.xml +++ b/clearing-parent/backend-api/src/main/resources/meta/data.xml @@ -444,7 +444,8 @@ - + + diff --git a/clearing-parent/db-scripts/src/main/resources/db/DATA.sql b/clearing-parent/db-scripts/src/main/resources/db/DATA.sql index 4c1228176..c6f943d20 100644 --- a/clearing-parent/db-scripts/src/main/resources/db/DATA.sql +++ b/clearing-parent/db-scripts/src/main/resources/db/DATA.sql @@ -866,6 +866,8 @@ INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5024, 'ACNT', 'Не у INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5025, 'ACNT', 'Необходимо указать ДЕПО счет.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME; +INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5026, 'ACNT', 'Счет %S не валютный') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME; + INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5200, 'BLNC', 'Общая ошибка модуля balance-service.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME; INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5210, 'BLNC', 'Клиринговая сессия неактивна.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME; 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 eae32d556..b0630c5b0 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 @@ -4,7 +4,7 @@ import ru.spcex.platform.utils.enumeration.IEnumKey; public enum AccountType implements IEnumKey { Clrn("CLRN"), Bank("BANK"), Info("INFO"), Tran("TRAN"), Corr("CORR"), Anlt("ANLT"), Depo("DEPO"), - Dtrn("DTRN"); + Dtrn("DTRN"), Curr("CURR"); 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 e4848fd9b..48cdf4822 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 @@ -184,6 +184,8 @@ public interface Consts { String REGISTRY_EXECUTION_REGISTER_ON_SAVE = "registry-execution-register-on-save"; String REGISTRY_EXECUTION_REGISTER_UPDATE = "registry-execution-register-update"; String REGISTRY_IDENTIFICATION_FUNDS = "registry-identification-funds"; + String DESTINATION_TRADING_CLEARING_REGISTRY_ON_TCRLIST_NEW = "registry-on-trading_clearing_registry_list-new"; + String DESTINATION_CURRENCY_PAIR_SECURITIES_NEW = "currency-pair-securities-new"; String DESTINATION_CURRENCY_PAIR_SECURITIES_UPDATE = "currency-pair-securities-update"; diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/clearing/CreateRegistryByTCRListRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/clearing/CreateRegistryByTCRListRequest.java new file mode 100644 index 000000000..c512c3ccf --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/clearing/CreateRegistryByTCRListRequest.java @@ -0,0 +1,18 @@ +package ru.spcex.clearing.platform.messaging.domain.cud.clearing; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public class CreateRegistryByTCRListRequest { + @JsonProperty + private List tradingClearingRegistryListIds; + + public List getTradingClearingRegistryListIds() { + return tradingClearingRegistryListIds; + } + + public void setTradingClearingRegistryListIds(List tradingClearingRegistryListIds) { + this.tradingClearingRegistryListIds = tradingClearingRegistryListIds; + } +}