diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java index f8581c91d..8a98e4b6f 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/BankAccountService.java @@ -40,7 +40,7 @@ public class BankAccountService extends QueueConsumer implements InitializingBea private final Imdg relationMap; private final IMessageResolver messageResolver; -// private final Imdg userImdg; + // private final Imdg userImdg; // private final Imdg userRoleSessionImdg; private final Function bankAccountNewRequestValidator; @@ -107,16 +107,16 @@ public class BankAccountService extends QueueConsumer implements InitializingBea log.debug("BankAccountNewRequest received"); BankAccount bankAccount = new BankAccount(); - bankAccount.setBankIdentificationCode(req.getBankIdentificationCode()); - bankAccount.setBankName(req.getBankName()); - bankAccount.setCorrespondentAccount(req.getCorrespondentAccount()); - bankAccount.setCorrespondentAccountName(req.getCorrespondentAccountName()); - bankAccount.setCurrency(req.getCurrency()); - bankAccount.setDestination(req.getDestination()); - bankAccount.setTaxpayerIdentificationNumber(req.getTaxpayerIdentificationNumber()); - bankAccount.setTaxRegistrationReasonCode(req.getTaxRegistrationReasonCode()); - bankAccount.setAccount(req.getAccount()); - bankAccount.setCompanyId(req.getCompanyId()); + bankAccount.setBankIdentificationCode(req.getBankIdentificationCode());// + bankAccount.setBankName(req.getBankName());// + bankAccount.setCorrespondentAccount(req.getCorrespondentAccount());//-- + bankAccount.setCorrespondentAccountName(req.getCorrespondentAccountName());//-- + bankAccount.setCurrency(req.getCurrency());//--/ + bankAccount.setDestination(req.getDestination());// + bankAccount.setTaxpayerIdentificationNumber(req.getTaxpayerIdentificationNumber());//-- + bankAccount.setTaxRegistrationReasonCode(req.getTaxRegistrationReasonCode());//-- + bankAccount.setAccount(req.getAccount());// + bankAccount.setCompanyId(req.getCompanyId());// Account account = new Account(); account.setAccount(req.account); diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/scheduler/LauncherController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/scheduler/LauncherController.java index a6e7bb993..1163100a5 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/scheduler/LauncherController.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/scheduler/LauncherController.java @@ -24,12 +24,13 @@ import ru.spcex.clearing.backendapi.security.KeycloakUtils; import ru.spcex.clearing.backendapi.service.IOperator; import ru.spcex.clearing.backendapi.service.IStateLoader; import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.platform.enumeration.Task; 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.IMessageResolver; -import java.time.Instant; import java.util.Collection; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -67,7 +68,7 @@ public class LauncherController extends AbstractQueueController { @RequestMapping(value = "/{task-code}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody public CudResponse add(@ApiParam(value = "Код задания из taskDictionary", required = true, example = "ABLK") - @PathVariable("task-code") String dictionaryName) throws ExecutionException, InterruptedException { + @PathVariable("task-code") String dictionaryName) throws ExecutionException, InterruptedException { AbstractDictionary taskEnum = taskDictionary.getSingleObjectByFieldValues(Map.of("code", dictionaryName)); if (taskEnum == null) { throw new NotFound404Exception("task dictionary element with code '" + dictionaryName + "'"); @@ -81,19 +82,36 @@ public class LauncherController extends AbstractQueueController { } launcherCommand.setTask(dictionaryName); launcherCommand.setUserId(user.getId()); - // пока здесь, это требуется для сохранения истории - saveLauncher(dictionaryName, user.getId()); //топики ограничиваются наличием в taskDictionary //подписываются на разные топики в разных модулях, см. ru.spcex.platform.enumeration.Task#topic - return processRequest("launcher-" + dictionaryName, launcherCommand); + return processRequest(Consts.LAUNCHER_NEW, launcherCommand); } - private void saveLauncher(String taskCode, Long userId) { - Launcher launcherCommandHistory = new Launcher(); - launcherCommandHistory.setTask(taskCode); - launcherCommandHistory.setSenderId(userId); - launcherCommandHistory.setCreated(Instant.now()); - launcherImdg.insert(launcherCommandHistory); + @ApiOperation(value = "create specific launcher.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)}) + @RequestMapping(value = "/specific", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public CudResponse addSpecific(@ApiParam(value = "Параметры команды в JSON формате.", required = true) + @RequestBody LauncherNew launcherNew) throws ExecutionException, InterruptedException { + AbstractDictionary taskEnum = taskDictionary.getSingleObjectByFieldValues(Map.of("code", launcherNew.getTask())); + if (taskEnum == null) { + throw new NotFound404Exception("task dictionary element with code '" + launcherNew.getTask() + "'"); + } + if (!Task.startOfClearing.getKey().equals(taskEnum.getCode())) { + throw new IllegalStateException(String.format("Task %s not support request with body", taskEnum.getCode())); + } + LauncherNew launcherCommand = new LauncherNew(); + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String username = KeycloakUtils.getUserNameFromAuthentication(authentication); + User user = userImdg.getSingleObjectByFieldValues(Map.of("identifier", username)); + if (user == null) { + throw new IllegalStateException("cannot obtain userId from logged in user " + username); + } + launcherCommand.setTask(launcherNew.getTask()); + launcherCommand.setUserId(user.getId()); + launcherCommand.setCompanyId(launcherNew.getCompanyId()); + launcherCommand.setSecurityId(launcherNew.getSecurityId()); + return processRequest(Consts.LAUNCHER_NEW, launcherCommand); } @Autowired diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/schedule/LauncherNew.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/schedule/LauncherNew.java index ce810d0e8..9977df62f 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/schedule/LauncherNew.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/schedule/LauncherNew.java @@ -6,9 +6,7 @@ import io.swagger.annotations.ApiModelProperty; import ru.spcex.clearing.backendapi.domain.actions.IAction; import ru.spcex.clearing.backendapi.errors.BackEndError; import ru.spcex.clearing.platform.messaging.domain.ActionType; -import ru.spcex.clearing.platform.messaging.domain.cud.reports.ReportWithPeriodRequest; import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest; -import ru.spcex.platform.enumeration.Task; import ru.spcex.platform.utils.enumeration.EnumMessage; import java.util.Collection; @@ -19,24 +17,26 @@ public class LauncherNew implements IAction { @ApiModelProperty(value = "Идентификатор единоличного исполнительного органа", example = "ABCD") @JsonProperty private String task; - + @ApiModelProperty(value = "Идентификатор инициатора", example = "1000") + @JsonProperty + private Long companyId; + @ApiModelProperty(value = "Идентификатор инструмента", example = "1000") + @JsonProperty + private Long securityId; @JsonIgnore private Long userId; @Override public Object toRequest() { - if (Task.createReport_GREP.equalsByKey(task)) { - ReportWithPeriodRequest reportCommand = new ReportWithPeriodRequest(); - reportCommand.setReportId(task); - return reportCommand; - } else { - LauncherCommandRequest taskRunnerCommandRequest = new LauncherCommandRequest(); - taskRunnerCommandRequest.setTaskName(task); - taskRunnerCommandRequest.setUserId(userId); - return taskRunnerCommandRequest; - } + LauncherCommandRequest taskRunnerCommandRequest = new LauncherCommandRequest(); + taskRunnerCommandRequest.setTaskName(task); + taskRunnerCommandRequest.setCompanyId(companyId); + taskRunnerCommandRequest.setSecurityId(securityId); + taskRunnerCommandRequest.setUserId(userId); + return taskRunnerCommandRequest; } + @ApiModelProperty(hidden = true) @Override public ActionType getActionType() { return ActionType.NEW; @@ -65,4 +65,20 @@ public class LauncherNew implements IAction { public void setUserId(Long userId) { this.userId = userId; } + + public Long getCompanyId() { + return companyId; + } + + public void setCompanyId(Long companyId) { + this.companyId = companyId; + } + + public Long getSecurityId() { + return securityId; + } + + public void setSecurityId(Long securityId) { + this.securityId = securityId; + } } diff --git a/clearing-parent/backend-api/src/main/resources/meta/meta.xml b/clearing-parent/backend-api/src/main/resources/meta/meta.xml index 058667e6d..c96d68e05 100644 --- a/clearing-parent/backend-api/src/main/resources/meta/meta.xml +++ b/clearing-parent/backend-api/src/main/resources/meta/meta.xml @@ -1,6 +1,6 @@ - + @@ -713,8 +713,8 @@ - - + + @@ -752,7 +752,7 @@ - + @@ -900,20 +900,20 @@ - + - - + + - - - - + + + + @@ -921,8 +921,8 @@ - - + + diff --git a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaConfig.java b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaConfig.java index d1c48b183..1e14bcf54 100644 --- a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaConfig.java +++ b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaConfig.java @@ -3,13 +3,20 @@ package ru.spcex.clearing.balance.config; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.producer.Producer; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import ru.spcex.clearing.balance.config.element.BalanceServiceSettings; +import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.config.KafkaConsumerFactory; import ru.spcex.clearing.platform.messaging.config.KafkaProducerFactory; +import ru.spcex.clearing.platform.messaging.service.RequestInfo; +import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgId; +import ru.spcex.platform.imdg.api.ImdgProvider; @Configuration public class KafkaConfig { @@ -21,8 +28,24 @@ public class KafkaConfig { } @Autowired - @Bean + @Bean("kafkaProducer") public Producer createProducer(BalanceServiceSettings settings) { return KafkaProducerFactory.producer(settings.getKafkaProducer()); } + + @Autowired + @Bean + public KafkaSender kafkaSender(@Qualifier("kafkaProducer") Producer kafkaProducer, + ImdgProvider imdgProvider) { + ImdgId imdgIdGenerator = imdgProvider.getImdgIdGenerator(); + return KafkaSender + .setup() + .producer(kafkaProducer) + .idGenerator(imdgIdGenerator::nextId) + .imdgProvider(s -> { + Imdg imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_RequestInfo, RequestInfo.class); + return imdg::insert; + }) + .build(); + } } diff --git a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaSenderConfig.java b/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaSenderConfig.java deleted file mode 100644 index a32e3bc5e..000000000 --- a/clearing-parent/balance-service/src/main/java/ru/spcex/clearing/balance/config/KafkaSenderConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.spcex.clearing.balance.config; - -import org.apache.kafka.clients.producer.Producer; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import ru.spcex.clearing.imdg.IMDGDistributedNames; -import ru.spcex.clearing.platform.messaging.service.RequestInfo; -import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; -import ru.spcex.platform.imdg.api.Imdg; -import ru.spcex.platform.imdg.api.ImdgId; -import ru.spcex.platform.imdg.api.ImdgProvider; - -@Configuration -public class KafkaSenderConfig { - - @Autowired - @Bean - public KafkaSender kafkaSender(Producer kafkaProducer, ImdgProvider imdgProvider) { - ImdgId imdgIdGenerator = imdgProvider.getImdgIdGenerator(); - return KafkaSender - .setup() - .producer(kafkaProducer) - .idGenerator(imdgIdGenerator::nextId) - .imdgProvider(s -> { - Imdg imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_RequestInfo, RequestInfo.class); - return imdg::insert; - }) - .build(); - } -} 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..087df3786 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 @@ -1,17 +1,25 @@ package ru.spcex.clearing.balance.service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; 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.number.BigDecimalUtil; import ru.spcex.platform.utils.validation.IValidator; import java.math.BigDecimal; @@ -23,15 +31,18 @@ import java.util.function.Function; @Component public class AccountBalanceService { + private final Logger log = LoggerFactory.getLogger(getClass()); 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 +96,101 @@ public class AccountBalanceService { if (b == null) return a; return a.add(b); } + + public void updateAccountBalanceByClearing(AccountBalanceClearingRequest req) { + ClearingCategory category = getClearingCategoryByCompanyId(req.getCompanyId()); + req.setFirstLegAmount(BigDecimalUtil.safeBD(req.getFirstLegAmount())); + 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()); + } + } + + 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); + log.trace("updating AccountBalance {} I CLRN", accountBalance.getId()); + BigDecimal previousFreeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getFreeBalanceAmount()); + BigDecimal previousChangeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getChangeBalanceAmount()); + BigDecimal previousBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getBalanceAmount()); + accountBalance.setFreeBalanceAmount(previousFreeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setChangeBalanceAmount(previousChangeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setBalanceAmount(previousBalanceAmount.subtract(firstLegAmount)); + accountBalance.setUpdated(Instant.now()); + log.trace("updateAccountCategoryIClrn: {}", accountBalance.getId()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryITran(BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(AccountType.Tran); + log.trace("updating AccountBalance {} I TRAN", accountBalance.getId()); + BigDecimal previousDebitAmount = BigDecimalUtil.safeBD(accountBalance.getDebitAmount()); + accountBalance.setDebitAmount(previousDebitAmount.add(firstLegAmount.abs())); + accountBalance.setUpdated(Instant.now()); + log.trace("updateAccountCategoryITran: {}", accountBalance.getId()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryVInfo(Long accountId, Long companyId, BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Info); + log.trace("updating AccountBalance {} V INFO", accountBalance.getId()); + BigDecimal previousFreeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getFreeBalanceAmount()); + BigDecimal changeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getChangeBalanceAmount()); + BigDecimal balanceAmount = BigDecimalUtil.safeBD(accountBalance.getBalanceAmount()); + accountBalance.setFreeBalanceAmount(previousFreeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setChangeBalanceAmount(changeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setBalanceAmount(balanceAmount.subtract(firstLegAmount)); + accountBalance.setUpdated(Instant.now()); + log.trace("updateAccountCategoryVInfo: {}", accountBalance.getId()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryVAnlt(BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(AccountType.Anlt); + log.trace("updating AccountBalance {} V ANLT", accountBalance.getId()); + BigDecimal previousFreeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getFreeBalanceAmount()); + BigDecimal previousChangeBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getChangeBalanceAmount()); + BigDecimal previousBalanceAmount = BigDecimalUtil.safeBD(accountBalance.getBalanceAmount()); + accountBalance.setFreeBalanceAmount(previousFreeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setChangeBalanceAmount(previousChangeBalanceAmount.subtract(firstLegAmount)); + accountBalance.setBalanceAmount(previousBalanceAmount.subtract(firstLegAmount)); + accountBalance.setUpdated(Instant.now()); + log.trace("updateAccountCategoryVAnlt: {}", accountBalance.getId()); + accountBalanceImdg.update(accountBalance); + } + + private void updateAccountCategoryBClrn(Long accountId, Long companyId, BigDecimal firstLegAmount) { + AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Clrn); + log.trace("updating AccountBalance {} B CLRN", accountBalance.getId()); + BigDecimal debitAmount = BigDecimalUtil.safeBD(accountBalance.getDebitAmount()); + accountBalance.setDebitAmount(plus(debitAmount, firstLegAmount.abs())); + accountBalance.setUpdated(Instant.now()); + log.trace("updateAccountCategoryBClrn: {}", accountBalance.getId()); + 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..37495b876 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,8 +15,10 @@ 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.domain.cud.common.CommonIdRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; import ru.spcex.platform.classes.base.interfaces.WithAccount; @@ -38,14 +40,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 +63,22 @@ 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) { + AccountBalanceClearingRequest payload = updateAccBalanceReq.getRequestPayload(); + log.debug("updating account balance for clearing baseRequest.id = {}; accountId = {}, companyId = {}, firstLegAmount = {}", updateAccBalanceReq.getId(), + payload.getAccountId(), payload.getCompanyId(), payload.getFirstLegAmount()); + accountBalanceService.updateAccountBalanceByClearing(payload); + CommonIdRequest commonIdRequest = new CommonIdRequest(); + commonIdRequest.setId(updateAccBalanceReq.getId()); + kafkaReqProducer.sendRequestToQueue(Consts.CONTINUE_CLEARING, commonIdRequest); + } + private void process(BaseRequest systemRequest) { StatementRequest statementRequest = systemRequest.getRequestPayload(); Collection sdfGroup; diff --git a/clearing-parent/clearing-service/pom.xml b/clearing-parent/clearing-service/pom.xml index aa206dcf4..1d7d1fd1a 100644 --- a/clearing-parent/clearing-service/pom.xml +++ b/clearing-parent/clearing-service/pom.xml @@ -51,6 +51,10 @@ assertj-core test + + org.mockito + mockito-core + diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/SortingConfig.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/SortingConfig.java new file mode 100644 index 000000000..b45a0085d --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/SortingConfig.java @@ -0,0 +1,34 @@ +package ru.spcex.clearing.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +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.IEnumKey; + +import java.util.Map; +import java.util.function.Function; + +@Configuration +public class SortingConfig { + private final Imdg clearingCategoryImdg; + + @Autowired + public SortingConfig(ImdgProvider imdgProvider) { + this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + } + + @Bean + public Function clearingMemberCategoryProvider() { + return companyId -> { + ClearingMemberCategory category = clearingCategoryImdg.getSingleObjectByFieldValues( + Map.of("companyId", companyId)); + return IEnumKey.getEnumByKey(ClearingCategory.class, + category.getClearingMemberCategory()); + }; + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/ValidationConfig.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/ValidationConfig.java new file mode 100644 index 000000000..f420c0839 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/config/ValidationConfig.java @@ -0,0 +1,65 @@ +package ru.spcex.clearing.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.company.relation.Relation; +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.service.validation.ClrngValidationStored; +import ru.spcex.clearing.service.validation.ExecutionDepositValidationRule; +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.enumeration.ClearingCategory; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.validation.IValidator; +import ru.spcex.platform.utils.validation.ValidatorImpl; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +@Configuration +public class ValidationConfig { + private final Map> imdgs; + + public ValidationConfig(ImdgProvider imdgProvider) { + this.imdgs = new HashMap<>(); + BiConsumer> addImdg = (s, aClass) -> imdgs.put(s, imdgProvider.getImdg(s, aClass)); + addImdg.accept(IMDGDistributedNames.Map_Relation, Relation.class); + addImdg.accept(IMDGDistributedNames.Map_Company, Company.class); + addImdg.accept(IMDGDistributedNames.Map_Account, Company.class); + addImdg.accept(IMDGDistributedNames.Map_AccountBalance, Company.class); + } + + /** + * чтобы во всех валидаторах был один экземпляр Imdg + */ + private Imdg getImdg(String key) { + return imdgs.get(key); + } + + private final BiConsumer, String> addImdg = (context, s) + -> context.addImdg(s, getImdg(s)); + + @Bean("executionDepositValidator") + public BiFunction executionDepositValidator() { + return (category, execDeposit) -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(execDeposit); + context.storeObject(ClrngValidationStored.categoryPredefined, category); + addImdg.accept(context, IMDGDistributedNames.Map_Relation); + addImdg.accept(context, IMDGDistributedNames.Map_Company); + addImdg.accept(context, IMDGDistributedNames.Map_Account); + addImdg.accept(context, IMDGDistributedNames.Map_AccountBalance); + return new ValidatorImpl<>(context, + ExecutionDepositValidationRule.ClearingIsAllowed, + ExecutionDepositValidationRule.AccountIsNotBlocked, + ExecutionDepositValidationRule.CompanyIsNotBlocked, + ExecutionDepositValidationRule.FinancialObligationSecurity); + }; + } + +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingErrorInternal.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingErrorInternal.java new file mode 100644 index 000000000..dfeee61f9 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingErrorInternal.java @@ -0,0 +1,21 @@ +package ru.spcex.clearing.error; + +import ru.spcex.platform.utils.enumeration.IEnumId; + +public enum ClearingErrorInternal implements IEnumId { + ClearingNotAllowed(1L), + AccountNotActive(2L), + CompanyNotActive(3L), + FinancialObligationNotSatisfied(4L), + ; + private final Long id; + + ClearingErrorInternal(Long id) { + this.id = id; + } + + @Override + public Long getId() { + return id; + } +} 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 new file mode 100644 index 000000000..179569ee6 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Clearing.java @@ -0,0 +1,234 @@ +package ru.spcex.clearing.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsMoney; +import ru.clearing.classes.statics.data.payment.PaymentInstruction; +import ru.spcex.clearing.error.ClearingErrorInternal; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountBalanceClearingRequest; +import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.service.builder.LiabilitiesClaimsAssetsCreator; +import ru.spcex.clearing.service.builder.LiabilitiesClaimsMoneyCreator; +import ru.spcex.clearing.service.builder.PaymentInstructionCreator; +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.log.ExceptionUtils; +import ru.spcex.platform.utils.validation.IValidator; + +import java.time.Instant; +import java.util.*; +import java.util.function.BiFunction; + +@Service +public class Clearing { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Imdg executionDepositImdg; + private final Imdg liabilitiesClaimsAssetsImdg; + private final Imdg liabilitiesClaimsMoneyImdg; + private final Imdg clearingCategoryImdg; + private final ExecutionDepositSorter sorter; + private final BiFunction validation; + private final PaymentInstructionCreator paymentInstructionCreator; + private final ImdgId idProvider; + private final KafkaSender kafka; + private final LiabilitiesClaimsAssetsCreator lbltsClmsAssetsCreator; + private final LiabilitiesClaimsMoneyCreator lbltsClmsMoneyCreator; + + private Long lastAccBalanceResponseId = 0L; + //при прохождении по выгруженным ExecutionDeposit, ошибочные статусы проставляются для + //контр сделок. В таком случае, в коллекции хранятся не синхронизированные с IMDG ExecutionDeposit + //для которых статус должен быть DENIED + //************ !!! NOT THREAD SAFE !!! ************ + private final Set deniedIds = new HashSet<>(); + private Long clearingSessionId; + + @Autowired + public Clearing(ImdgProvider imdgProvider, + ExecutionDepositSorter sorter, PaymentInstructionCreator paymentInstructionCreator, + BiFunction validation, KafkaSender kafka, LiabilitiesClaimsAssetsCreator liabilitiesClaimsAssetsCreator, LiabilitiesClaimsMoneyCreator lbltsClmsMoneyCreator) { + this.executionDepositImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ExecutionDeposit, ExecutionDeposit.class); + this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + this.liabilitiesClaimsAssetsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_LiabilitiesClaimsAssets, LiabilitiesClaimsAssets.class); + this.liabilitiesClaimsMoneyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_LiabilitiesClaimsMoney, LiabilitiesClaimsMoney.class); + this.paymentInstructionCreator = paymentInstructionCreator; + this.idProvider = imdgProvider.getImdgIdGenerator(); + this.sorter = sorter; + this.validation = validation; + this.kafka = kafka; + this.lbltsClmsAssetsCreator = liabilitiesClaimsAssetsCreator; + this.lbltsClmsMoneyCreator = lbltsClmsMoneyCreator; + } + + public void startClearing() { + clearingSessionId = idProvider.nextId(); + log.info("startClearing clearingSessionId = {}. loading ExecutionDeposits", clearingSessionId); + //выгружаем ExecutionDeposit с пустым sessionId + Collection execDeposits = executionDepositImdg.getCollectionObjectsBySQL("sessionId = null"); + log.info("loaded {} ExecutionDeposits", execDeposits.size()); + //делаем группировку ExecutionDeposit по категории и компании, делаем сортировку + LinkedHashMap>> executionDeposits = sorter.sortExecutionDeposit(execDeposits); + + executionDeposits.forEach((category, companies) -> { + for (List companyExecDeposits : companies) { + runChecksSetStatus(category, companyExecDeposits); + } + }); + executionDeposits.forEach((category, companies) -> { + for (List companyExecDeposits : companies) { + processAllowedExecutionDeposit(category, companyExecDeposits); + } + }); + } + + //кладем в порядке 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())) { + log.trace("executionDeposit {} was already denied", execDeposit.getId()); + continue; + } + if (financialError) { + log.trace("executionDeposit {} FinancialObligationNotSatisfied was present earlier for this company; setting DENIED status", execDeposit.getId()); + updateDenied(execDeposit); + continue; + } + IValidator validator = validation.apply(category, execDeposit); + Optional errorMsg = validator.tillFirstError(); + if (errorMsg.isPresent() && !ClearingErrorInternal.FinancialObligationNotSatisfied.equals(errorMsg.get().getSubject())) { + log.trace("executionDeposit {} setting DENIED status: {}", execDeposit.getId(), errorMsg.get().getSubject().nameOrId()); + updateDenied(execDeposit); + continue; + } else if (errorMsg.isPresent()) { + log.trace("executionDeposit {} setting DENIED status: {}", execDeposit.getId(), errorMsg.get().getSubject().nameOrId()); + financialError = true; + updateDenied(execDeposit); + continue; + } + updateAllowed(execDeposit); + log.trace("executionDeposit {} was updated ALLOWED", execDeposit.getId()); + } + } + + private void updateDenied(ExecutionDeposit execDeposit) { + execDeposit.setCoverageStatus(Allowed.DENIED.getKey()); + execDeposit.setUpdated(Instant.now()); + executionDepositImdg.update(execDeposit); + log.trace("executionDeposit {} was updated DENIED", execDeposit.getId()); + //также необходимо установить статус 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); + log.trace("executionDeposit {} matching executionDeposit {} was updated DENIED", execDeposit.getId(), matchedExecDeposit.getId()); + deniedIds.add(matchedExecDeposit.getId()); + } + } + + private void updateAllowed(ExecutionDeposit execDeposit) { + execDeposit.setCoverageStatus(Allowed.ALLOWED.getKey()); + 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())) { + log.trace("Skipping processing executionDeposit {} was denied as matching", execDeposit.getId()); + continue; + } + if (!Allowed.ALLOWED.getKey().equals(execDeposit.getCoverageStatus())) { + log.trace("Skipping processing executionDeposit {} was denied", execDeposit.getId()); + continue; + } + //send request to kafka, wait for a reply + synchronized (this) { + AccountBalanceClearingRequest request = new AccountBalanceClearingRequest(); + request.setAccountId(execDeposit.getAccountId()); + request.setCompanyId(execDeposit.getCompanyId()); + request.setFirstLegAmount(execDeposit.getFirstLegAmount()); + Long sentRequestId = kafka.sendRequestToQueue(Consts.BALANCE_ACCOUNT_UPDATE, request); + log.trace("sent request to kafka, requestId: {}", sentRequestId); + try { + wait(10000); + //если на этом месте произошла ошибка - непонятно как обрабатывать + //т.к. этом может быть уже вторая часть сделки, для первой accountBalance уже был обновлен + //либо вне зависимости от того какая часть сделки, accountBalance на самом деле мог быть + //обновлен, и была проблема была с сетью/недоступностью кафки etc. + } catch (InterruptedException e) { + log.error(ExceptionUtils.getStackTrace(e)); + } + if (!Objects.equals(lastAccBalanceResponseId, sentRequestId)) { + log.error("FATAL: last response from AccountBalance update from Kafka ID was {}, but sent ID was {}", + lastAccBalanceResponseId, + sentRequestId); + continue; + } + } + LiabilitiesClaimsAssets lca = lbltsClmsAssetsCreator.createLiabilitiesClaimsAssets(category, execDeposit); + liabilitiesClaimsAssetsImdg.insert(lca); + log.trace("LiabilitiesClaimsAssets {} was created", lca.getId()); + Optional lcmFirstLegFound = lbltsClmsMoneyCreator.searchLcmBySettlementDate(lca.getAccountId(), lca.getCompanyId(), lca.getSettlementDate()); + var wrapperFirstLegCreated = new Object() {LiabilitiesClaimsMoney firstLegCreated;}; + lcmFirstLegFound.ifPresentOrElse(lcm -> { + log.trace("LiabilitiesClaimsMoney first leg {} was found", lcm.getId()); + lbltsClmsMoneyCreator.updateFirstLegLcm(lcm, lca, category); + liabilitiesClaimsMoneyImdg.update(lcm); + }, () -> { + LiabilitiesClaimsMoney lcmFirstLeg = lbltsClmsMoneyCreator.createFirstLegLcm(lca, category); + liabilitiesClaimsMoneyImdg.insert(lcmFirstLeg); + log.trace("LiabilitiesClaimsMoney first leg {} was created", lcmFirstLeg.getId()); + wrapperFirstLegCreated.firstLegCreated = lcmFirstLeg; + }); + Optional lcmSecondLegFound = lbltsClmsMoneyCreator.searchLcmByRefundDate(lca.getAccountId(), lca.getCompanyId(), lca.getRefundDate()); + lcmSecondLegFound.ifPresentOrElse(lcm -> { + log.trace("LiabilitiesClaimsMoney second leg {} was found", lcm.getId()); + lbltsClmsMoneyCreator.updateSecondLegLcm(lcm, lca, category); + liabilitiesClaimsMoneyImdg.update(lcm); + }, () -> { + LiabilitiesClaimsMoney lcmSecondLeg = lbltsClmsMoneyCreator.createSecondLegLcm(lca, category); + liabilitiesClaimsMoneyImdg.insert(lcmSecondLeg); + log.trace("LiabilitiesClaimsMoney second leg {} was created", lcmSecondLeg.getId()); + }); + lbltsClmsAssetsCreator.updateLca(lca, lcmFirstLegFound.orElse(wrapperFirstLegCreated.firstLegCreated)); + liabilitiesClaimsAssetsImdg.update(lca); + log.trace("creating paymentInstructions"); + + List paymentInstructions = paymentInstructionCreator.createAndSavePaymentInstructions(lca, Collections.singleton(category)); + paymentInstructionCreator.updateFieldLiabilitiesClaimsAssetsByPayment(lca, paymentInstructions, Collections.singleton(category)); + liabilitiesClaimsAssetsImdg.update(lca); + var firstLegLiabilitiesClaimsMoney = lcmFirstLegFound.orElseGet(() -> wrapperFirstLegCreated.firstLegCreated); + log.trace("updating LiabilitiesClaimsMoney by payment"); + paymentInstructionCreator.updateFieldLiabilitiesClaimsMoneyByPayment(firstLegLiabilitiesClaimsMoney, paymentInstructions, Collections.singleton(category)); + liabilitiesClaimsMoneyImdg.update(firstLegLiabilitiesClaimsMoney); + log.trace("LiabilitiesClaimsMoney first leg {} was updated", firstLegLiabilitiesClaimsMoney.getId()); + } + } + + public void setLastAccBalanceResponseId(Long lastAccBalanceResponseId) { + this.lastAccBalanceResponseId = lastAccBalanceResponseId; + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ClearingService.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ClearingService.java index 58c25acfe..0dc6ac3b9 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ClearingService.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ClearingService.java @@ -5,8 +5,9 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; import ru.spcex.platform.utils.log.ExceptionUtils; import java.util.concurrent.ExecutorService; @@ -20,13 +21,18 @@ public class ClearingService implements DisposableBean { private final SdfCreatorBySTLDPayment sdfCreator; private final PaymentUpdateBySdf04 paymentUpdater; private final VerificationResultComponent verificationResultComponent; + private final Clearing clearing; + private final ExecutionDepositComponent executionDepositComponent; @Autowired public ClearingService(SdfCreatorBySTLDPayment sdfCreator, PaymentUpdateBySdf04 paymentUpdater, - VerificationResultComponent verificationResultComponent) { + VerificationResultComponent verificationResultComponent, Clearing clearing, + ExecutionDepositComponent executionDepositComponent) { this.sdfCreator = sdfCreator; this.paymentUpdater = paymentUpdater; this.verificationResultComponent = verificationResultComponent; + this.executionDepositComponent = executionDepositComponent; + this.clearing = clearing; this.executor = Executors.newSingleThreadExecutor(); } @@ -69,4 +75,35 @@ public class ClearingService implements DisposableBean { log.debug("Shutdown {}", getClass().getSimpleName()); executor.shutdown(); } + + public void executeClearing() { + log.info("start clearing"); + executor.execute(() -> { + try { + clearing.startClearing(); + } catch (Throwable e) { + log.error("{}", ExceptionUtils.getStackTrace(e)); + } + }); + } + + public void continueClearing(BaseRequest event) { + synchronized (clearing) { + CommonIdRequest requestPayload = event.getRequestPayload(); + clearing.setLastAccBalanceResponseId(requestPayload.getId()); + clearing.notifyAll(); + } + } + + public void executeSTrade() { + log.info("start STrade check for ExecutionDeposit"); + executor.execute(() -> { + try { + executionDepositComponent.processNewTS(); + } catch (Throwable e) { + log.error("{}", ExceptionUtils.getStackTrace(e)); + } + }); + } + } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/EventsReceiver.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/EventsReceiver.java index f7911379f..9eb639b9c 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/EventsReceiver.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/EventsReceiver.java @@ -5,6 +5,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.clearing.Sdf04Request; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; import ru.spcex.platform.enumeration.Task; @@ -31,6 +32,15 @@ public class EventsReceiver extends QueueConsumer implements InitializingBean { callback(LauncherCommandRequest.class) .setConsumer(event -> clearingService.executeVerification()) .forDestination(Task.getVerification.topic(), callbacks::put); + callback(Object.class) //todo check Object suitable + .setConsumer(event -> clearingService.executeClearing()) + .forDestination(Task.startOfClearing.topic(), callbacks::put); + callback(CommonIdRequest.class) + .setConsumer(clearingService::continueClearing) + .forDestination(Consts.CONTINUE_CLEARING, callbacks::put); + callback(Object.class) + .setConsumer(event -> clearingService.executeSTrade()) + .forDestination(Task.getOfTrades.topic(), callbacks::put); init(); } } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecDepositWCategory.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecDepositWCategory.java new file mode 100644 index 000000000..91f745949 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecDepositWCategory.java @@ -0,0 +1,26 @@ +package ru.spcex.clearing.service; + +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.spcex.platform.enumeration.ClearingCategory; + +public class ExecDepositWCategory { + private final ExecutionDeposit executionDeposit; + private final ClearingCategory clearingMemberCategory; + + public ExecDepositWCategory(ExecutionDeposit executionDeposit, ClearingCategory clearingMemberCategory) { + this.executionDeposit = executionDeposit; + this.clearingMemberCategory = clearingMemberCategory; + } + + public ExecutionDeposit getExecutionDeposit() { + return executionDeposit; + } + + public ClearingCategory getClearingMemberCategory() { + return clearingMemberCategory; + } + + public Long getCompanyId() { + return executionDeposit.getCompanyId(); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecutionDepositComponent.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecutionDepositComponent.java index e16d34736..a97c97046 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecutionDepositComponent.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/ExecutionDepositComponent.java @@ -1,6 +1,5 @@ package ru.spcex.clearing.service; -import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.kafka.clients.producer.RecordMetadata; @@ -10,15 +9,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import ru.clearing.classes.statics.data.account.Account; -import ru.clearing.classes.statics.data.account.AccountBalance; -import ru.clearing.classes.statics.data.clearing.VerificationResult; import ru.clearing.classes.statics.data.company.Company; import ru.clearing.classes.statics.data.execution.ExecutionDeposit; import ru.clearing.classes.statics.data.misc.Listing; import ru.clearing.classes.statics.data.misc.STrade; -import ru.clearing.classes.statics.data.sdf.SDf01; import ru.clearing.classes.statics.data.security.Security; import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.error.ClearingException; @@ -26,12 +21,11 @@ import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.ActionType; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.DealRegisterNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.securitites.MoneyMarketSecurityNewRequest; -import ru.spcex.platform.classes.base.interfaces.WithId; -import ru.spcex.platform.enumeration.AccountType; +import ru.spcex.platform.classes.base.SpcexObjectBase; import ru.spcex.platform.enumeration.Allowed; import ru.spcex.platform.enumeration.Market; -import ru.spcex.platform.enumeration.ResultStatuses; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgId; import ru.spcex.platform.imdg.api.ImdgProvider; @@ -40,17 +34,13 @@ import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.log.ExceptionUtils; import ru.spcex.platform.utils.time.TimeUtil; -import ru.spcex.clearing.platform.messaging.domain.cud.registry.CoveredDealRegisterNewRequest; - import java.math.BigDecimal; -import java.math.RoundingMode; import java.time.Instant; import java.time.LocalDate; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.function.Function; import java.util.stream.Collectors; /** @@ -96,8 +86,9 @@ public class ExecutionDepositComponent { /** * Сбрасывать каждый день в 01:00:01 "0 1 0 1 * ?" */ - @Scheduled(cron = "${clearing-service.scheduler.check-s-trade}") + @Scheduled(cron = "0 1 0 1 * ?") public void resetTradingDay() { + log.trace("Recheck today trading day for search STrade. Current state: tradeNum={}, tradeDat={}", tradeNum, tradingDay); Instant today = TimeUtil.localDateToInstant(LocalDate.now()); if (tradingDay == null || !tradingDay.equals(today)) { tradeNum = -1L; @@ -107,9 +98,9 @@ public class ExecutionDepositComponent { } public void processNewTS() { - Long tradeNum = -1L; // todo уточнить как он обновляется + log.debug("Start check new S_TRADE after {}", tradingDay); ImdgPredicateBuilder pb = sTradeImdg.predicateBuilder(); - ImdgPredicate sql = pb.and(pb.greater("tradeNum",tradeNum), pb.greatEqual("tradeDateTime", tradingDay)); + ImdgPredicate sql = pb.greatEqual("tradeDateTime", tradingDay); Collection sTrades = sTradeImdg.getCollectionObjectsByPredicate(sql); log.info("Found {} new s_trade with trade_num>{}", sTrades.size(), tradeNum); @@ -121,12 +112,13 @@ public class ExecutionDepositComponent { // Выявление новых сделок необходимо выполнить следующие контрольные проверки: // Проверить все инструменты. + Set secCodesOfSecurity; { Set secCodesOfSTrade = sTrades.stream().map(STrade::getSecCode).filter(Objects::nonNull).collect(Collectors.toSet()); log.debug("Verify {} instruments for {} STrade's.", secCodesOfSTrade.size(), sTrades.size()); ImdgPredicate allIn = securityImdg.predicateBuilder().in("securitySymbol", secCodesOfSTrade.toArray(new String[0])); Collection foundSecurities = securityImdg.getCollectionObjectsByPredicate(allIn); - Set secCodesOfSecurity = foundSecurities.stream().map(Security::getSecuritySymbol).filter(Objects::nonNull).collect(Collectors.toSet()); + secCodesOfSecurity = foundSecurities.stream().map(Security::getSecuritySymbol).filter(Objects::nonNull).collect(Collectors.toSet()); if (secCodesOfSecurity.containsAll(secCodesOfSTrade)) { log.debug("All {} Security found by {} secCodes from STrade", secCodesOfSecurity.size(), secCodesOfSTrade.size()); @@ -135,59 +127,49 @@ public class ExecutionDepositComponent { notFoundSymbol.removeAll(secCodesOfSecurity); log.info("Found only {} Security by {} secCodes from STrade. Not found: {}", secCodesOfSecurity.size(), secCodesOfSTrade.size(), notFoundSymbol); - createNewSecurities(notFoundSymbol); - log.info("Stop till they all will be created"); - - auditMessage("В security нет записей с securitySymbol", notFoundSymbol); - return; } } - Long generationId = idGenerator.nextId(); - log.info("generationId = {}", generationId); - for (STrade trade:sTrades) { - log.trace("Check s_trade[{}].tradeNum={}", trade.getId(), trade.getTradeNum()); + LocalDate today = LocalDate.now(); + for (STrade trade : sTrades) { + log.trace("Check s_trade[{}].tradeNum={} on date {}", trade.getId(), trade.getTradeNum(), today); Collection existsEDeposit = executionDepositImdg.getCollectionObjectsByFieldValues(Map.of( "exchangeExecutionId", trade.getTradeNum(), - "exchangeExecutionTime", trade.getTradeDateTime() + "clearingDate", today )); if (existsEDeposit.isEmpty()) { log.trace("S_TRADE[{}] new", trade.getId()); - ExecutionDeposit newED = null; + + // Проверка secCode + if (!secCodesOfSecurity.contains(trade.getSecCode())) { + log.error("Error {}: STrade[{}].secCode={} not found", + ClearingError.RecordNotFound.getId(), trade.getId(), trade.getSecCode()); + continue; + } + + ExecutionDeposit newED; try { - newED = createExecutionDeposit(trade, Allowed.ALLOWED/*todo уточнить момент заполнения*/, generationId); - verification(newED); + newED = createExecutionDeposit(trade, null, null); executionDepositImdg.insert(newED); sendNotification(newED); } catch (ClearingException ce) { auditMessage(ce); } catch (Exception e) { - if (newED != null) { - newED.setCoverageStatus(Allowed.DENIED.getKey()); - } - log.error("When create new ExecutionDeposit by STrade[{}]", trade.getId()); + log.error("When create new ExecutionDeposit by STrade[{}] error: {}", trade.getId(), ExceptionUtils.getStackTrace(e)); } } else { - long[] idToLong = existsEDeposit.stream().mapToLong(ed-> ed.getId()).toArray(); - log.warn("S_TRADE[{}] already has executionDeposit: {}", trade.getId(), Arrays.toString(idToLong)); + long[] idToLong = existsEDeposit.stream().mapToLong(SpcexObjectBase::getId).toArray(); + log.trace("S_TRADE[{}] already has executionDeposit: {}", trade.getId(), Arrays.toString(idToLong)); } } - Long newMaxTradeNum = sTrades.stream().mapToLong(STrade::getTradeNum).max().orElseGet(()-> tradeNum); - log.debug("Next tradeNum is {}", newMaxTradeNum); + Long newMaxTradeNum = sTrades.stream().mapToLong(STrade::getTradeNum).max().orElseGet(() -> tradeNum); + log.info("Process completed. Next tradeNum is {}", newMaxTradeNum); } - protected void verification(ExecutionDeposit forED) throws ClearingException { - /*todo Рассчитанные в КС контрольные суммы (общее количество сделок и суммарный объем заключенных сделок в денежном выражении) - должны совпадать со значениями, рассчитанными Торговой системой: - count(execution[tradingDay]) = count (trade_arqua) - */ - // использовать ли VerificationResultComponent для сверки или здесь код добавить. - - } protected void auditMessage(ClearingException ce) { log.error("AUDIT error code {}: {}", ce.getEnumMsg(), ce.getMessage()); @@ -201,71 +183,12 @@ public class ExecutionDepositComponent { log.error("audit \"clearing-service\", errorText: {}", txt); } - /** - * в очередь kafka для модуля securities-service сообщение о добавлении инструмента с параметром securitySymbol=s_trade.sec_code - * @param newSymbolRequest - */ - protected void createNewSecurities(Collection newSymbolRequest) { - final String destination = Consts.DESTINATION_MONEY_MARKET_SECURITY_NEW; - List symbolRequests = new ArrayList<>(newSymbolRequest); // чтобы в случае ошибки отобразить номер в логе - List> sendAll = new ArrayList<>(symbolRequests.size()); - for (String newSymbol: symbolRequests) { - if (newSymbol == null || newSymbol.isEmpty()) { - log.warn("Empty SecuritySumbol"); - } else { - MoneyMarketSecurityNewRequest requestPayload = new MoneyMarketSecurityNewRequest(); - requestPayload.setSecuritySymbol(newSymbol); - - BaseRequest request = new BaseRequest<>(); - request.setId(idGenerator.nextId()); - request.setActionType(ActionType.NEW); - request.setRequestPayload(requestPayload); - -// saveRequestToStorage(destination, request); //сохраняет данные о запросе в хранилище - log.trace("Send to {} new symbol \"{}\" ", destination, newSymbol); - Future send = kafka.send(new ProducerRecord<>(destination, request)); - sendAll.add(send); - } - } - int i = 0; - for (Future future: sendAll) { - try { - future.get(); // get exception - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - String about = i < symbolRequests.size() ? symbolRequests.get(i) : "(out of range i=" + i + ")"; - log.warn("Thread interrupted! On send symbol \"{}\"", about); - throw new RuntimeException(e); - } catch (ExecutionException e) { - String about = i < symbolRequests.size() ? symbolRequests.get(i) : "(out of range i=" + i + ")"; - log.error("Error send message for symbol \"{}\" to {}: {}", destination, - about, ExceptionUtils.getStackTrace(e.getCause() == null ? e : e.getCause())); - } - i++; - } - } - protected void sendNotification(ExecutionDeposit forED) { - final String destination = Consts.REGISTRY_COVERED_DEAL_REGISTER_NEW; - CoveredDealRegisterNewRequest requestPayload = new CoveredDealRegisterNewRequest(); + final String destination = Consts.REGISTRY_DEAL_REGISTER_NEW; + DealRegisterNewRequest requestPayload = new DealRegisterNewRequest(); requestPayload.setExecutionId(forED.getId()); -// requestPayload.setCompanyFullName(forED.getCompanyFullName()); - requestPayload.setTradingDate(forED.getTradingDate()); requestPayload.setExchangeExecutionId(forED.getExchangeExecutionId()); - requestPayload.setExchangeExecutionTime(forED.getExchangeExecutionTime()); - requestPayload.setSecuritySymbol(forED.getSecuritySymbol()); - requestPayload.setSecurityFullName(forED.getSecurityFullName()); -// requestPayload.setSellerFullName(forED.getSellerFullName()); -// requestPayload.setSellerClearingCode(forED.getSellerClearingCode()); -// String requestPayload.setSellerAccount(forED.getAccountId()); -// String requestPayload.setBuyerFullName(forED.getBuyerFullName()); -// requestPayload.setBuyerClearingCode(forED.getBuyerClearingCode()); -// String requestPayload.setBuyerAccount(forED.getBuyerAccount()); -// BigDecimal requestPayload.setAmount(forED.getAmount()); - requestPayload.setId(idGenerator.nextId()); - requestPayload.setCreatedAt(forED.getCreated()); - requestPayload.setUpdatedAt(forED.getUpdated()); - requestPayload.setClearingDate(forED.getClearingDate()); +// requestPayload.setId(idGenerator.nextId()); BaseRequest request = new BaseRequest<>(); request.setId(idGenerator.nextId()); diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/LauncherCommandReceiver.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/LauncherCommandReceiver.java new file mode 100644 index 000000000..06d5aa4ef --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/LauncherCommandReceiver.java @@ -0,0 +1,33 @@ +package ru.spcex.clearing.service; + +import org.apache.kafka.clients.consumer.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest; +import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.platform.enumeration.Task; + +@Service +public class LauncherCommandReceiver extends QueueConsumer implements InitializingBean { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ClearingService clearingService; + @Autowired + public LauncherCommandReceiver(Consumer kafkaQueue, + ClearingService clearingService) { + super(kafkaQueue); + this.clearingService = clearingService; + } + + @Override + public void afterPropertiesSet() { + callback(LauncherCommandRequest.class) + .setConsumer(action -> clearingService.executeVerification()) + .forDestination(Task.createOrderConfirm.topic(), callbacks::put); // CORC + + init(); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/SdfCreatorBySTLDPayment.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/SdfCreatorBySTLDPayment.java index 9d1f6bd25..cc1b8a607 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/SdfCreatorBySTLDPayment.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/SdfCreatorBySTLDPayment.java @@ -11,10 +11,10 @@ import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.clearing.SdfClearingRequest; import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.service.builder.Sdf03Builder; +import ru.spcex.clearing.service.builder.Sdf11Builder; import ru.spcex.clearing.service.order.PaymentBatchInfo; import ru.spcex.clearing.service.order.PaymentInstructionSorter; -import ru.spcex.clearing.service.order.Sdf03Builder; -import ru.spcex.clearing.service.order.Sdf11Builder; import ru.spcex.platform.enumeration.TransactionStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgId; diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsAssetsCreator.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsAssetsCreator.java new file mode 100644 index 000000000..85876ea63 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsAssetsCreator.java @@ -0,0 +1,76 @@ +package ru.spcex.clearing.service.builder; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsMoney; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.AccountType; +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.IEnumKey; + +import java.time.Instant; +import java.time.LocalDate; + +@Component +public class LiabilitiesClaimsAssetsCreator { + private final Imdg accountImdg; + private final Imdg companyImdg; + + @Autowired + public LiabilitiesClaimsAssetsCreator(ImdgProvider imdgProvider) { + this.accountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class); + this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + } + + + public LiabilitiesClaimsAssets createLiabilitiesClaimsAssets(ClearingCategory category, ExecutionDeposit executionDeposit) { + LiabilitiesClaimsAssets liabilitiesClaimsAssets = new LiabilitiesClaimsAssets(); + liabilitiesClaimsAssets.setCompanyId(executionDeposit.getCompanyId()); + liabilitiesClaimsAssets.setAccountId(executionDeposit.getAccountId()); + Account account = accountImdg.getSingleObjectByID(executionDeposit.getAccountId()); + liabilitiesClaimsAssets.setAccountType(account.getAccountType()); + liabilitiesClaimsAssets.setAccount(account.getAccount()); + AccountType accType = IEnumKey.getEnumByKey(AccountType.class, account.getAccountType()); + boolean iClrn = ClearingCategory.I.equals(category) && AccountType.Clrn.equals(accType); + boolean vInfo = ClearingCategory.V.equals(category) && AccountType.Info.equals(accType); + boolean bClrn = ClearingCategory.B.equals(category) && AccountType.Clrn.equals(accType); + + if (iClrn || vInfo) { + liabilitiesClaimsAssets.setLiabilitiesQuantity(executionDeposit.getFirstLegAmount()); + } + if (bClrn) { + liabilitiesClaimsAssets.setClaimsQuantity(executionDeposit.getSecondLegAmount()); + } + liabilitiesClaimsAssets.setCurrency(executionDeposit.getSettlementCurrency()); + liabilitiesClaimsAssets.setSettlementDate(executionDeposit.getFirstLegSettlementDate()); + liabilitiesClaimsAssets.setTradingDate(executionDeposit.getTradingDate()); + liabilitiesClaimsAssets.setRefundDate(executionDeposit.getSecondLegSettlementDate()); + liabilitiesClaimsAssets.setPrice(executionDeposit.getPrice()); + liabilitiesClaimsAssets.setSecurityId(executionDeposit.getSecurityId()); + Company company = companyImdg.getSingleObjectByID(executionDeposit.getCompanyId()); + liabilitiesClaimsAssets.setTradingCode(company.getTradingCode()); + liabilitiesClaimsAssets.setClearingCode(company.getClearingCode()); + liabilitiesClaimsAssets.setShortName(company.getShortName()); + liabilitiesClaimsAssets.setContract(executionDeposit.getSecuritySymbol()); + liabilitiesClaimsAssets.setFullName(company.getFullName()); + //todo liabilitiesClaimsAssets.setLiabilitiesClaimsMoneyId(company.getFullName()); + //fixme liabilitiesClaimsAssets.setClearingStatus(); + //todo liabilitiesClaimsAssets.setPaymentId(); + //todo liabilitiesClaimsAssets.setRefundPaymentId(); + liabilitiesClaimsAssets.setCreated(Instant.now()); + liabilitiesClaimsAssets.setClearingDate(LocalDate.now()); + return liabilitiesClaimsAssets; + } + + public void updateLca(LiabilitiesClaimsAssets lca, + LiabilitiesClaimsMoney firstLeg) { + lca.setLiabilitiesClaimsMoneyId(firstLeg.getId()); + lca.setUpdated(Instant.now()); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsMoneyCreator.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsMoneyCreator.java new file mode 100644 index 000000000..f6fd17c5c --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/LiabilitiesClaimsMoneyCreator.java @@ -0,0 +1,104 @@ +package ru.spcex.clearing.service.builder; + +import org.springframework.stereotype.Component; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsMoney; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.ClearingCategory; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Optional; + +import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD; + +@Component +public class LiabilitiesClaimsMoneyCreator { + private final Imdg liabilitiesClaimsMoneyImdg; + private final static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public LiabilitiesClaimsMoneyCreator(ImdgProvider imdgProvider) { + this.liabilitiesClaimsMoneyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_LiabilitiesClaimsMoney, LiabilitiesClaimsMoney.class); + } + + public Optional searchLcmBySettlementDate(Long accountId, Long companyId, LocalDate settlementDate) { + String sql = String.format("accountId=%d and companyId=%d and settlementDate=%s", + accountId, companyId, formatter.format(settlementDate)); + LiabilitiesClaimsMoney liabilitiesClaimsMoney = liabilitiesClaimsMoneyImdg.getSingleObjectBySQL(sql); + return Optional.ofNullable(liabilitiesClaimsMoney); + } + + public void updateFirstLegLcm(LiabilitiesClaimsMoney liabilitiesClaimsMoney, + LiabilitiesClaimsAssets liabilitiesClaimsAssets, ClearingCategory category) { + BigDecimal prevLAmount = safeBD(liabilitiesClaimsMoney.getLiabilitiesAmount()); + BigDecimal prevClaimsAmount = safeBD(liabilitiesClaimsMoney.getClaimsAmount()); + BigDecimal lcaLiabilitiesQuantity = safeBD(liabilitiesClaimsAssets.getLiabilitiesQuantity()); + BigDecimal lcaClaimsQuantity = safeBD(liabilitiesClaimsAssets.getClaimsQuantity()); + liabilitiesClaimsMoney.setLiabilitiesAmount(prevLAmount.add(lcaLiabilitiesQuantity)); + liabilitiesClaimsMoney.setClaimsAmount(prevClaimsAmount.add(lcaClaimsQuantity)); + } + + + public LiabilitiesClaimsMoney createFirstLegLcm(LiabilitiesClaimsAssets lca, ClearingCategory category) { + LiabilitiesClaimsMoney lcm = new LiabilitiesClaimsMoney(); + lcm.setCompanyId(lca.getCompanyId()); + lcm.setAccountId(lca.getAccountId()); + lcm.setAccountType(lca.getAccountType()); + lcm.setAccount(lca.getAccount()); + if (category.equals(ClearingCategory.I) || category.equals(ClearingCategory.V)) { + lcm.setLiabilitiesAmount(lca.getLiabilitiesQuantity()); + } else if (category.equals(ClearingCategory.B)) { + lcm.setClaimsAmount(lca.getClaimsQuantity()); + } + lcm.setSettlementDate(lca.getSettlementDate()); + lcm.setTradingDate(lca.getTradingDate()); + lcm.setCurrency(lca.getCurrency()); + lcm.setTradingCode(lca.getTradingCode()); + lcm.setShortName(lca.getShortName()); + lcm.setFullName(lca.getFullName()); + lcm.setCreated(lca.getCreated()); + lcm.setClearingDate(LocalDate.now()); + return lcm; + } + + public Optional searchLcmByRefundDate(Long accountId, Long companyId, LocalDate refundDate) { + String sql = String.format("accountId=%d and companyId=%d and settlementDate=%s", + accountId, companyId, formatter.format(refundDate)); + LiabilitiesClaimsMoney liabilitiesClaimsMoney = liabilitiesClaimsMoneyImdg.getSingleObjectBySQL(sql); + return Optional.ofNullable(liabilitiesClaimsMoney); + } + + public LiabilitiesClaimsMoney createSecondLegLcm(LiabilitiesClaimsAssets lca, ClearingCategory category) { + LiabilitiesClaimsMoney lcm = new LiabilitiesClaimsMoney(); + lcm.setCompanyId(lca.getCompanyId()); + lcm.setAccountId(lca.getAccountId()); + lcm.setAccountType(lca.getAccountType()); + lcm.setAccount(lca.getAccount()); + if (category.equals(ClearingCategory.I) || category.equals(ClearingCategory.V)) { + lcm.setClaimsAmount(lca.getClaimsQuantity()); + } else if (category.equals(ClearingCategory.B)) { + lcm.setLiabilitiesAmount(lca.getLiabilitiesQuantity()); + } + lcm.setSettlementDate(lca.getRefundDate()); + lcm.setTradingDate(lca.getTradingDate()); + lcm.setCurrency(lca.getCurrency()); + lcm.setTradingCode(lca.getTradingCode()); + lcm.setShortName(lca.getShortName()); + lcm.setFullName(lca.getFullName()); + lcm.setCreated(lca.getCreated()); + lcm.setClearingDate(lca.getRefundDate()); + return lcm; + } + + public void updateSecondLegLcm(LiabilitiesClaimsMoney lcm, LiabilitiesClaimsAssets lca, ClearingCategory category) { + BigDecimal prevLiabilitiesAmount = safeBD(lcm.getLiabilitiesAmount()); + BigDecimal prevClaimsAmount = safeBD(lcm.getClaimsAmount()); + BigDecimal lcaLiabilitiesQuantity = safeBD(lca.getLiabilitiesQuantity()); + BigDecimal lcaClaimsQuantity = safeBD(lca.getClaimsQuantity()); + lcm.setLiabilitiesAmount(prevLiabilitiesAmount.add(lcaLiabilitiesQuantity)); + lcm.setClaimsAmount(prevClaimsAmount.add(lcaClaimsQuantity)); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/PaymentInstructionCreator.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/PaymentInstructionCreator.java new file mode 100644 index 000000000..60dc70007 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/PaymentInstructionCreator.java @@ -0,0 +1,533 @@ +package ru.spcex.clearing.service.builder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.company.CompanySymbols; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsMoney; +import ru.clearing.classes.statics.data.payment.PaymentInstruction; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.enumeration.*; +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.time.TimeUtil; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +/* +todo использовать так: + paymentInstructionCreator.createAndSavePaymentInstructions() + paymentInstructionCreator.updateLiabilitiesClaimsAssetsByPaymentInstruction(); + paymentInstructionCreator.updateLiabilitiesClaimsMoneyByPaymentInstruction(); + + */ +@Service +public class PaymentInstructionCreator { + protected static final Long SPVB_ID = 1L; // СПВБ + protected static final Long PRC_ID = 2L; // 2 "НКО АО ПРЦ" + private static final DateTimeFormatter DATE_FORMATTER_ddMMyy = DateTimeFormatter.ofPattern("ddMMyy"); + + private final Logger log = LoggerFactory.getLogger(getClass()); + + protected final ImdgId idGenerator; + protected final Imdg liabilitiesClaimsAssetsImdg; + protected final Imdg companySymbolsImdg; + protected final Imdg companyImdg; + protected final Imdg accountImdg; + protected final Imdg paymentInstructionImdg; + protected final Imdg liabilitiesClaimsMoneyImdg; + + protected AtomicLong documentNumberId = new AtomicLong(0L); // порядковый номер (сквозной по всем компаниям за день + protected LocalDate documentNumberResetAt; + + @Autowired + public PaymentInstructionCreator(ImdgProvider imdgProvider) { + this.liabilitiesClaimsAssetsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_LiabilitiesClaimsAssets, LiabilitiesClaimsAssets.class); + this.companySymbolsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class); + this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + this.accountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class); + this.paymentInstructionImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class); + this.liabilitiesClaimsMoneyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_LiabilitiesClaimsMoney, LiabilitiesClaimsMoney.class); + this.idGenerator = imdgProvider.getImdgIdGenerator(); + } + + /** + * 2.2.2. Выполнить Создание paymentInstruction по liabilitiesClaimsAssets + * @return Созданные PaymentInstruction, может быть несколько. Они уже сохранены в IMDG. + */ + public List createAndSavePaymentInstructions(LiabilitiesClaimsAssets liabilitiesClaimsAssets, + Collection clearingCategory) { + List newPaymentInstructions = createPaymentInstructions(liabilitiesClaimsAssets, clearingCategory); + log.trace("Created {} PaymentInstruction by LiabilitiesClaimsAssets[{}]. Do save ti IMDG.", + newPaymentInstructions.size(), liabilitiesClaimsAssets.getId()); + for (PaymentInstruction instruction : newPaymentInstructions) { + paymentInstructionImdg.insert(instruction); + } + return newPaymentInstructions; + } + + /** + * Создаёт и сохраняет + * + * @param liabilitiesClaimsAssets + * @param clearingCategory + * @return + */ + protected List createPaymentInstructions(LiabilitiesClaimsAssets liabilitiesClaimsAssets, + Collection clearingCategory) { + List newPaymentInstructions = new ArrayList<>(); + Instant now = Instant.now(); + + // I + if (clearingCategory.contains(ClearingCategory.I)) { + log.debug("By liabilitiesClaimsAssets[{}] create for category I 1 record", liabilitiesClaimsAssets.getId()); + PaymentInstruction payment1 = new PaymentInstruction(); + PaymentInstruction payment2 = new PaymentInstruction(); + + payment1.setId(idGenerator.nextId()); + payment1.setCreated(now); + payment1.setClearingDate(TimeUtil.toLocalDate(now)); + payment2.setId(idGenerator.nextId()); + payment2.setCreated(now); + payment2.setClearingDate(TimeUtil.toLocalDate(now)); + + payment1.setSenderId(liabilitiesClaimsAssets.getCompanyId()); + payment2.setSenderId(SPVB_ID); // СПВБ + + payment1.setAddresseeId(SPVB_ID); // СПВБ + String sqlLCA = String.format("securityId = %s and companyId <> %s", + liabilitiesClaimsAssets.getSecurityId(), liabilitiesClaimsAssets.getCompanyId()); + LiabilitiesClaimsAssets counterLiabilitiesClaimsAssets = liabilitiesClaimsAssetsImdg.getSingleObjectBySQL(sqlLCA); + if (counterLiabilitiesClaimsAssets == null) { + log.warn("Counter LiabilitiesClaimsAssets not found for : {}", sqlLCA); + } else { + payment2.setAddresseeId(counterLiabilitiesClaimsAssets.getCompanyId()); + } + + String symbol1 = selectSymbolValue(payment1.getAddresseeId(), CompanySymbol.BIC); + if (symbol1 == null) { + log.warn("CompanySymbols BIC not found for companyId={}", payment1.getAddresseeId()); + } else { + payment1.setAdresseeBic(symbol1); + } + String symbol2 = selectSymbolValue(payment2.getAddresseeId(), CompanySymbol.BIC); + if (symbol2 == null) { + log.warn("CompanySymbols BIC not found for companyId={}", payment2.getAddresseeId()); + } else { + payment2.setAdresseeBic(symbol2); + } + + Company companyPRC = companyImdg.getSingleObjectByID(PRC_ID); // 2 "НКО АО ПРЦ" + String companyPRCName = null; + if (companyPRC == null) { + log.warn("Company.id=2 not found"); + } else { + companyPRCName = companyPRC.getShortName(); + } + payment1.setPayeeBankName(companyPRCName); + payment2.setPayeeBankName(companyPRCName); + + String symbolPRC = selectSymbolValue(PRC_ID, CompanySymbol.BIC); // 2 "НКО АО ПРЦ" + payment1.setPayeeBic(symbolPRC); + payment2.setPayeeBic(symbolPRC); + + payment1.setAddresseeBankName(companyPRCName); + payment2.setAddresseeBankName(companyPRCName); + + payment1.setPaymentDate(TimeUtil.localDateToInstant(liabilitiesClaimsAssets.getSettlementDate())); + payment2.setPaymentDate(TimeUtil.localDateToInstant(liabilitiesClaimsAssets.getSettlementDate())); + + payment1.setPaymentPurpose("Размещение депозита " + liabilitiesClaimsAssets.getContract()); + payment2.setPaymentPurpose("Размещение депозита " + liabilitiesClaimsAssets.getContract()); + + payment1.setSettlementDate(liabilitiesClaimsAssets.getSettlementDate()); + payment2.setSettlementDate(liabilitiesClaimsAssets.getSettlementDate()); + + Long amount = liabilitiesClaimsAssets.getLiabilitiesQuantity() == null ? null : liabilitiesClaimsAssets.getLiabilitiesQuantity().abs().longValue(); + payment1.setCreditLegAmount(amount); + payment2.setCreditLegAmount(amount); + payment1.setDebitLegAmount(amount); + payment2.setDebitLegAmount(amount); + + payment1.setCreditLegAccountId(liabilitiesClaimsAssets.getAccountId()); + String accSql = String.format("companyId = %s AND accountType=TRAN AND accountStatus=ACTV AND processingSign=ALWD", + payment2.getSenderId()); + Account anotherAcc = accountImdg.getSingleObjectBySQL(accSql); + if (anotherAcc == null) { + log.warn("Account not found: {}", accSql); + } else { + payment2.setCreditLegAccountId(anotherAcc.getId()); + } + + payment1.setCreditCsAccount(null); + payment2.setCreditCsAccount(null); + + { + Account acc1 = accountImdg.getSingleObjectByID(payment1.getCreditLegAccountId()); + if (acc1 != null) { + payment1.setCreditLegAccount(acc1.getAccount()); + } + Account acc2 = accountImdg.getSingleObjectByID(payment2.getCreditLegAccountId()); + if (acc2 != null) { + payment1.setCreditLegAccount(acc2.getAccount()); + } + } + + { + Account acc1 = selectAccount(payment1.getAddresseeId(), AccountType.Tran, Status.Active, Allowed.ALLOWED); + if (acc1 != null) { + payment1.setDebitLegAccountId(acc1.getId()); + payment1.setDebitLegAccount(acc1.getAccount()); + } + Account acc2 = selectAccount(payment2.getAddresseeId(), AccountType.Clrn, Status.Active, Allowed.ALLOWED); + if (acc2 != null) { + payment2.setDebitLegAccountId(acc2.getId()); + payment2.setDebitLegAccount(acc2.getAccount()); + } + } + + payment1.setDebitCsAccount(null); + payment2.setDebitCsAccount(null); + +// payment1.setCreditLegDirection(InOutDirection.out.getKey()); +// payment2.setCreditLegDirection(InOutDirection.out.getKey()); +// payment1.setDebitLegDirection(InOutDirection.in.getKey()); +// payment2.setDebitLegDirection(InOutDirection.in.getKey()); + + payment1.setCreditLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + payment2.setCreditLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + payment1.setDebitLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + payment2.setDebitLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + + payment1.setTransactionStatus(TransactionStatus.stld.getKey()); + payment2.setTransactionStatus(TransactionStatus.stld.getKey()); + + + payment1.setDocumentNumber(nextDocumentNumber(liabilitiesClaimsAssets, payment1)); + payment2.setDocumentNumber(nextDocumentNumber(liabilitiesClaimsAssets, payment2)); + + newPaymentInstructions.add(payment1); + newPaymentInstructions.add(payment2); + } else { + // V / B + if (clearingCategory.contains(ClearingCategory.V)) { + log.debug("By liabilitiesClaimsAssets[{}] create for category V 1 record", liabilitiesClaimsAssets.getId()); + PaymentInstruction payment1 = new PaymentInstruction(); + payment1.setId(idGenerator.nextId()); + payment1.setCreated(now); + payment1.setClearingDate(TimeUtil.toLocalDate(now)); + + payment1.setSenderId(SPVB_ID); // СПВБ + + String sqlLCA = String.format("securityId = %s and companyId <> %s", + liabilitiesClaimsAssets.getSecurityId(), liabilitiesClaimsAssets.getCompanyId()); + LiabilitiesClaimsAssets counterLiabilitiesClaimsAssets = liabilitiesClaimsAssetsImdg.getSingleObjectBySQL(sqlLCA); + if (counterLiabilitiesClaimsAssets == null) { + log.warn("Counter LiabilitiesClaimsAssets not found for : {}", sqlLCA); + } else { + payment1.setAddresseeId(counterLiabilitiesClaimsAssets.getCompanyId()); + } + + String symbol1 = selectSymbolValue(payment1.getAddresseeId(), CompanySymbol.BIC); + if (symbol1 == null) { + log.warn("CompanySymbols BIC not found for companyId={}", payment1.getAddresseeId()); + } else { + payment1.setAdresseeBic(symbol1); + } + + Company companyPRC = companyImdg.getSingleObjectByID(PRC_ID); // 2 "НКО АО ПРЦ" + String companyPRCName = null; + if (companyPRC == null) { + log.warn("Company.id=2 not found"); + } else { + companyPRCName = companyPRC.getShortName(); + } + payment1.setPayeeBankName(companyPRCName); + + String symbolPRC = selectSymbolValue(PRC_ID, CompanySymbol.BIC); // 2 "НКО АО ПРЦ" + payment1.setPayeeBic(symbolPRC); + + payment1.setAddresseeBankName(companyPRCName); + + payment1.setPaymentDate(TimeUtil.localDateToInstant(liabilitiesClaimsAssets.getSettlementDate())); + + payment1.setPaymentPurpose("Размещение депозита " + liabilitiesClaimsAssets.getContract()); + + payment1.setSettlementDate(liabilitiesClaimsAssets.getSettlementDate()); + + Long amount = liabilitiesClaimsAssets.getLiabilitiesQuantity() == null ? null : liabilitiesClaimsAssets.getLiabilitiesQuantity().abs().longValue(); + payment1.setCreditLegAmount(amount); + payment1.setDebitLegAmount(amount); + + { + String accSql = String.format("companyId = %s AND accountType=ANLT AND accountStatus=ACTV AND processingSign=ALWD", + payment1.getSenderId()); + Account acc1 = accountImdg.getSingleObjectBySQL(accSql); + if (acc1 == null) { + log.warn("Account not found: {}", accSql); + } else { + payment1.setCreditLegAccountId(acc1.getId()); + payment1.setCreditLegAccount(acc1.getAccount()); + } + } + + payment1.setCreditCsAccount(null); + + { + Account acc1 = selectAccount(payment1.getAddresseeId(), AccountType.Corr, Status.Active, Allowed.ALLOWED); + if (acc1 != null) { + payment1.setDebitLegAccountId(acc1.getId()); + payment1.setDebitLegAccount(acc1.getAccount()); + } + } + + payment1.setDebitCsAccount(null); + +// payment1.setCreditLegDirection(InOutDirection.out.getKey()); +// payment1.setDebitLegDirection(InOutDirection.in.getKey()); + + payment1.setCreditLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + payment1.setDebitLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + + payment1.setTransactionStatus(TransactionStatus.stld.getKey()); + + + payment1.setDocumentNumber(nextDocumentNumber(liabilitiesClaimsAssets, payment1)); + + newPaymentInstructions.add(payment1); + } else // Для V,B только 1 PaymentInstruction + if (clearingCategory.contains(ClearingCategory.B)) { + log.debug("By liabilitiesClaimsAssets[{}] create for category B 1 record", liabilitiesClaimsAssets.getId()); + PaymentInstruction payment1 = new PaymentInstruction(); + payment1.setId(idGenerator.nextId()); + payment1.setCreated(now); + payment1.setClearingDate(TimeUtil.toLocalDate(now)); + + payment1.setSenderId(liabilitiesClaimsAssets.getCompanyId()); + + payment1.setAddresseeId(SPVB_ID); // СПВБ + + String symbol1 = selectSymbolValue(payment1.getAddresseeId(), CompanySymbol.BIC); + if (symbol1 == null) { + log.warn("CompanySymbols BIC not found for companyId={}", payment1.getAddresseeId()); + } else { + payment1.setAdresseeBic(symbol1); + } + + Company companyPRC = companyImdg.getSingleObjectByID(PRC_ID); // 2 "НКО АО ПРЦ" + String companyPRCName = null; + if (companyPRC == null) { + log.warn("Company.id=2 not found"); + } else { + companyPRCName = companyPRC.getShortName(); + } + payment1.setPayeeBankName(companyPRCName); + + String symbolPRC = selectSymbolValue(PRC_ID, CompanySymbol.BIC); // 2 "НКО АО ПРЦ" + payment1.setPayeeBic(symbolPRC); + + payment1.setAddresseeBankName(companyPRCName); + + payment1.setPaymentDate(TimeUtil.localDateToInstant(liabilitiesClaimsAssets.getSettlementDate())); + + payment1.setPaymentPurpose("Размещение депозита " + liabilitiesClaimsAssets.getContract()); + + payment1.setSettlementDate(liabilitiesClaimsAssets.getSettlementDate()); + + Long amount = liabilitiesClaimsAssets.getLiabilitiesQuantity() == null ? null : liabilitiesClaimsAssets.getLiabilitiesQuantity().abs().longValue(); + payment1.setCreditLegAmount(amount); + payment1.setDebitLegAmount(amount); + + { + String accSql = String.format("companyId = %s AND accountType=ANLT AND accountStatus=ACTV AND processingSign=ALWD", + payment1.getSenderId()); + Account acc1 = accountImdg.getSingleObjectBySQL(accSql); + if (acc1 == null) { + log.warn("Account not found: {}", accSql); + } else { + payment1.setCreditLegAccountId(acc1.getId()); + payment1.setCreditLegAccount(acc1.getAccount()); + } + } + + payment1.setCreditCsAccount(null); + + { + Account acc1 = selectAccount(payment1.getAddresseeId(), AccountType.Corr, Status.Active, Allowed.ALLOWED); + if (acc1 != null) { + payment1.setDebitLegAccountId(acc1.getId()); + payment1.setDebitLegAccount(acc1.getAccount()); + } + } + + payment1.setDebitCsAccount(null); + +// payment1.setCreditLegDirection(InOutDirection.out.getKey()); +// payment1.setDebitLegDirection(InOutDirection.in.getKey()); + + payment1.setCreditLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + payment1.setDebitLegCurrencyCode(liabilitiesClaimsAssets.getCurrency()); + + payment1.setTransactionStatus(TransactionStatus.stld.getKey()); + + + payment1.setDocumentNumber(nextDocumentNumber(liabilitiesClaimsAssets, payment1)); + + newPaymentInstructions.add(payment1); + } + } + return newPaymentInstructions; + } + + protected String nextDocumentNumber(LiabilitiesClaimsAssets liabilitiesClaimsAssets, PaymentInstruction paymentInstruction) { + LocalDate nowD = LocalDate.now(); + if (documentNumberResetAt == null || documentNumberResetAt.isBefore(nowD)) synchronized (this) { + long oldNum = documentNumberId.get(); // reset optimistic + if (oldNum > 0) { + while (!documentNumberId.compareAndSet(oldNum, 0)) { + oldNum = documentNumberId.get(); + if (oldNum < 2) break; + } + } + documentNumberResetAt = nowD; + } + String paymentDate = DATE_FORMATTER_ddMMyy.format(TimeUtil.toLocalDate(paymentInstruction.getPaymentDate())); + String num = String.format("%s/%s/%s/%s", + liabilitiesClaimsAssets.getContract(), paymentDate, + paymentInstruction.getSenderId(), documentNumberId.incrementAndGet() + ); + return num; + } + + protected String selectSymbolValue(Long companyId, CompanySymbol symbol) { + CompanySymbols cSymbol = companySymbolsImdg.getSingleObjectByFieldValues(Map.of( + "companyId", companyId, + "companySymbol", symbol.getKey())); + if (cSymbol == null) { + return null; + } else { + return cSymbol.getCompanySymbolValue(); + } + } + + protected Account selectAccount(Long companyId, AccountType accountType, Status accountStatus, Allowed processingSign) { + Account account = accountImdg.getSingleObjectByFieldValues(Map.of( + "companyId", companyId, + "accountType", accountType.getKey(), + "accountStatus", accountStatus.getKey(), + "processingSign", processingSign.getKey() + )); + return account; + } + + + /** + * 2.2.2.1. Выполнить Изменение liabilitiesClaimsAssets по paymentInstruction. + */ + public void updateFieldLiabilitiesClaimsAssetsByPayment(LiabilitiesClaimsAssets liabilitiesClaimsAssets, + List paymentInstructions, + Collection clearingCategory) { + + PaymentInstruction paymentIAny=paymentInstructions.iterator().next(); + + if (clearingCategory.contains(ClearingCategory.I) || clearingCategory.contains(ClearingCategory.V)) { + BigDecimal amount = paymentIAny.getCreditLegAmount() == null ? BigDecimal.ZERO : new BigDecimal((Long)paymentIAny.getCreditLegAmount()); +// При обновлении (по paymentInstruction и категория I и счет CLRN):=liabilitiesClaimsAssets.liabilitiesQuantity - paymentInstruction.creditLeg_amount +// При обновлении (по paymentInstruction и категория V и счет INFO):=liabilitiesClaimsAssets.liabilitiesQuantity - paymentInstruction.creditLeg_amount + amount = liabilitiesClaimsAssets.getLiabilitiesQuantity().subtract(amount); + liabilitiesClaimsAssets.setLiabilitiesQuantity(amount); + } + if (clearingCategory.contains(ClearingCategory.B)) { + BigDecimal amount = paymentIAny.getCreditLegAmount() == null ? BigDecimal.ZERO : new BigDecimal((Long)paymentIAny.getCreditLegAmount()); + // При обновлении (по paymentInstruction и категория B и счет CLRN): из paymentInstruction.creditLeg_amount + liabilitiesClaimsAssets.setLiabilitiesQuantity(amount); + } + + if (clearingCategory.contains(ClearingCategory.I) || clearingCategory.contains(ClearingCategory.V)) { + BigDecimal amount = new BigDecimal((Long)paymentIAny.getDebitLegAmount()); + liabilitiesClaimsAssets.setClaimsQuantity(amount); + } + if (clearingCategory.contains(ClearingCategory.B)) { + BigDecimal amount = new BigDecimal((Long)paymentIAny.getDebitLegAmount()); + amount = liabilitiesClaimsAssets.getClaimsQuantity().subtract(amount); + liabilitiesClaimsAssets.setClaimsQuantity(amount); + } + +// //на базе успешного/неуспешного изменения по paymentInstruction +// if (TransactionStatus.fail.equalsByKey(paymentIAny.getTransactionStatus())) { +// liabilitiesClaimsAssets.setClearingStatus(ClearingStatus.FAIL.getKey()); +// } else if (TransactionStatus.ok.equalsByKey(paymentIAny.getTransactionStatus())) { +// liabilitiesClaimsAssets.setClearingStatus(ClearingStatus.OK.getKey()); +// } + + liabilitiesClaimsAssets.setUpdated(Instant.now()); + } + + + /** + * 2.2.2.2. Выполнить Изменение liabilitiesClaimsMoney по paymentInstruction. + */ + public LiabilitiesClaimsMoney updateLiabilitiesClaimsMoneyByPaymentInstruction(LiabilitiesClaimsAssets liabilitiesClaimsAssets, + List paymentInstructions, + Collection clearingCategory) { + Long lmId = liabilitiesClaimsAssets.getLiabilitiesClaimsMoneyId(); + log.debug("Update LiabilitiesClaimsMoney[{}] by paymentInstructions {}", lmId, paymentInstructions.stream().map(SpcexObjectBase::getId).collect(Collectors.toList())); + LiabilitiesClaimsMoney liabilitiesClaimsMoney = liabilitiesClaimsMoneyImdg.getSingleObjectByID(lmId); // update data for update + if (liabilitiesClaimsMoney == null) { + throw new RuntimeException("IMDG not found LiabilitiesClaimsMoney.id=" + lmId); + } + if (!Objects.equals(liabilitiesClaimsMoney.getSettlementDate(), liabilitiesClaimsAssets.getSettlementDate())) { + log.warn("Date not match: LiabilitiesClaimsMoney[{}].settlementDate={} and LiabilitiesClaimsAssets[{}].settlementDate={}. It is first leg?", + liabilitiesClaimsMoney.getId(), liabilitiesClaimsMoney.getSettlementDate(), liabilitiesClaimsAssets.getId(), liabilitiesClaimsAssets.getSettlementDate() + ); + } + updateFieldLiabilitiesClaimsMoneyByPayment(liabilitiesClaimsMoney, paymentInstructions, clearingCategory); + + liabilitiesClaimsMoneyImdg.update(liabilitiesClaimsMoney); + return liabilitiesClaimsMoney; + } + + /** + * Обновление liabilitiesClaimsMoney первой ноги по liabilitiesClaimsAssets + * @param liabilitiesClaimsMoney обновить этот объект (firstLeg) + * @param paymentInstructions + * @param clearingCategory + * @return были внесены изменения в liabilitiesClaimsMoney + */ + public boolean updateFieldLiabilitiesClaimsMoneyByPayment(LiabilitiesClaimsMoney liabilitiesClaimsMoney, + List paymentInstructions, + Collection clearingCategory) { + boolean modified = false; + + PaymentInstruction paymentIAny=paymentInstructions.iterator().next(); + + if ((clearingCategory.contains(ClearingCategory.I) || clearingCategory.contains(ClearingCategory.V))) { + BigDecimal amount = liabilitiesClaimsMoney.getLiabilitiesAmount().subtract(new BigDecimal((Long)paymentIAny.getCreditLegAmount())); + liabilitiesClaimsMoney.setLiabilitiesAmount(amount); + modified = true; + } + + if ((clearingCategory.contains(ClearingCategory.B))) { + BigDecimal amount = liabilitiesClaimsMoney.getClaimsAmount().subtract(new BigDecimal((Long)paymentIAny.getDebitLegAmount())); + liabilitiesClaimsMoney.setClaimsAmount(amount); + modified = true; + } + + if (modified) { + liabilitiesClaimsMoney.setUpdated(Instant.now()); + } + return modified; + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf03Builder.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf03Builder.java similarity index 98% rename from clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf03Builder.java rename to clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf03Builder.java index d91755610..628619fd0 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf03Builder.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf03Builder.java @@ -1,4 +1,4 @@ -package ru.spcex.clearing.service.order; +package ru.spcex.clearing.service.builder; import ru.clearing.classes.statics.data.payment.PaymentInstruction; import ru.clearing.classes.statics.data.sdf.SDf03; diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf11Builder.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf11Builder.java similarity index 98% rename from clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf11Builder.java rename to clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf11Builder.java index c609b133e..6818a776a 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/Sdf11Builder.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/builder/Sdf11Builder.java @@ -1,4 +1,4 @@ -package ru.spcex.clearing.service.order; +package ru.spcex.clearing.service.builder; import ru.clearing.classes.statics.data.payment.PaymentInstruction; import ru.clearing.classes.statics.data.sdf.SDf11; diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/ExecutionDepositSorter.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/ExecutionDepositSorter.java new file mode 100644 index 000000000..4a17cba7e --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/ExecutionDepositSorter.java @@ -0,0 +1,111 @@ +package ru.spcex.clearing.service.order; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.spcex.platform.enumeration.ClearingCategory; +import ru.spcex.platform.enumeration.MoneyFlowSide; +import ru.spcex.platform.utils.collection.Pair; +import ru.spcex.platform.utils.enumeration.IEnumKey; + +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Component +public class ExecutionDepositSorter { + private Function clearingMemberCategoryProvider; + + @Autowired + public void setClearingMemberCategoryProvider(Function clearingMemberCategoryProvider) { + this.clearingMemberCategoryProvider = clearingMemberCategoryProvider; + } + + public LinkedHashMap>> sortExecutionDeposit(CollectionexecDeposits) { + //отсортировали все записи по категориям + // → внутри каждой подгруппы по категориям сортируем по компаниям + // → обрабатываем почередно все записи первой компании + // → когда по первой компании записи закончились, начинаем обработавать все записи второй компании и так далее + // → когда по первой категории закончились записи для всех компаниий, + // начинаем аналогично обработавать все записи второй категории и так далее). + + //сортируем по категориям + Map> categoryGrouping = execDeposits.stream().map(executionDeposit -> { + ClearingCategory category = clearingMemberCategoryProvider.apply(executionDeposit.getCompanyId()); + return new Pair<>(category, executionDeposit); + }).collect(Collectors.groupingBy(Pair::getFirst, Collectors.mapping(Pair::getSecond, Collectors.toList()))); + + //внутри категорий сортируем по компаниям + LinkedHashMap>> byCategoryByCompany = new LinkedHashMap<>(); + Consumer takeByClearingCategoryAndSortByCompanyId = clearingCategory -> { + List wrappers = categoryGrouping.remove(clearingCategory); + if (wrappers != null) { + List> batches = wrappers.stream() + .collect(Collectors.groupingBy(ExecutionDeposit::getCompanyId)) + .entrySet() + .stream() + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .toList(); + byCategoryByCompany.put(clearingCategory, batches); + } + }; + //кладем в порядке I, V, B + takeByClearingCategoryAndSortByCompanyId.accept(ClearingCategory.I); + takeByClearingCategoryAndSortByCompanyId.accept(ClearingCategory.V); + takeByClearingCategoryAndSortByCompanyId.accept(ClearingCategory.B); + //для всех остальных порядок не важен, будут последними + categoryGrouping.keySet().forEach(takeByClearingCategoryAndSortByCompanyId); + + //метод для дополнительной сортировки внутри групп по категории и компании + BiConsumer> additionalSorting = + (clearingCategory, comparator) -> { + List> allExecDepositsForCategory = byCategoryByCompany.get(clearingCategory); + if (allExecDepositsForCategory != null) { + allExecDepositsForCategory.forEach(singleCompanyBatch -> singleCompanyBatch.sort(comparator)); + } + }; + //применяем дополнительные сортировки + //для V сначала Sell, потом Buy, в первую очередь должны обрабатываться новые сделки с наименьшими объемами + additionalSorting.accept(ClearingCategory.V, + firstSellThenBuy.thenComparing(ExecutionDeposit::getFirstLegAmount)); + //для V сначала Buy, потом Sell, exchangeExecutionId - возрастающий + additionalSorting.accept(ClearingCategory.B, + firstBuyThenSell.thenComparing(ExecutionDeposit::getExchangeExecutionId)); + return byCategoryByCompany; + } + + public static Comparator firstSellThenBuy = Comparator.comparing(wrapper -> { + MoneyFlowSide side = IEnumKey.getEnumByKey(MoneyFlowSide.class, wrapper.getSide()); + if (side == null) return Integer.MAX_VALUE; + switch (side) { + case BUY -> { + return 1; + } + case SELL -> { + return 0; + } + default -> { + return Integer.MAX_VALUE; + } + } + }); + + public static Comparator firstBuyThenSell = Comparator.comparing(wrapper -> { + MoneyFlowSide side = IEnumKey.getEnumByKey(MoneyFlowSide.class, wrapper.getSide()); + if (side == null) return Integer.MAX_VALUE; + switch (side) { + case BUY -> { + return 0; + } + case SELL -> { + return 1; + } + default -> { + return Integer.MAX_VALUE; + } + } + }); +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentBatchInfo.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentBatchInfo.java index 4f96a3856..fbf555bde 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentBatchInfo.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentBatchInfo.java @@ -1,7 +1,7 @@ package ru.spcex.clearing.service.order; import ru.clearing.classes.statics.data.payment.PaymentInstruction; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.utils.enumeration.EnumMessage; import java.util.Collection; @@ -13,13 +13,13 @@ public class PaymentBatchInfo { private List initialOrder; private List fromClearingToBank; private List fromBankToClearing; - private ClearingMemberCategoryD categoryD; + private ClearingCategory categoryD; private EnumMessage error; public Stream getOrderedPaymentInstructions() { Function, Stream> safeStream = paymentInstructions -> paymentInstructions != null ? paymentInstructions.stream() : Stream.empty(); - if (categoryD.equals(ClearingMemberCategoryD.B)) { + if (categoryD.equals(ClearingCategory.B)) { return safeStream.apply(initialOrder); } else { return Stream.concat(safeStream.apply(fromClearingToBank), safeStream.apply(fromBankToClearing)); @@ -43,11 +43,11 @@ public class PaymentBatchInfo { this.fromBankToClearing = fromBankToClearing; } - public ClearingMemberCategoryD getCategoryD() { + public ClearingCategory getCategoryD() { return categoryD; } - public void setCategoryD(ClearingMemberCategoryD categoryD) { + public void setCategoryD(ClearingCategory categoryD) { this.categoryD = categoryD; } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentInstructionSorter.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentInstructionSorter.java index c898213d2..031bcf128 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentInstructionSorter.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/order/PaymentInstructionSorter.java @@ -10,7 +10,7 @@ import ru.clearing.classes.statics.data.payment.PaymentInstruction; import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.platform.enumeration.AccountType; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.enumeration.TransactionStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; @@ -46,15 +46,15 @@ public class PaymentInstructionSorter { List result = new ArrayList<>(2); log.info("processing PaymentInstruction's generationId={} senderId={} size={}", generationId, senderId, payments.size()); Collection cmcList = clrngMmbrImdg.getCollectionObjectsByFieldValues(Map.of("companyId", senderId)); - Collection companyCategory = cmcList.stream() + Collection companyCategory = cmcList.stream() .filter(cat -> cat.getClearingMemberCategory() != null) - .map(cat -> IEnumKey.getEnumByKey(ClearingMemberCategoryD.class, cat.getClearingMemberCategory())) + .map(cat -> IEnumKey.getEnumByKey(ClearingCategory.class, cat.getClearingMemberCategory())) .collect(Collectors.toSet()); - for (ClearingMemberCategoryD categoryValue : companyCategory) { + for (ClearingCategory categoryValue : companyCategory) { log.debug("For category {}", categoryValue); PaymentBatchInfo batchInfo = new PaymentBatchInfo(); batchInfo.setCategoryD(categoryValue); - if (ClearingMemberCategoryD.I.equals(categoryValue)) { + if (ClearingCategory.I.equals(categoryValue)) { EnumMessage error = null; List aList = new ArrayList<>(); List bList = new ArrayList<>(); @@ -118,7 +118,7 @@ public class PaymentInstructionSorter { batchInfo.setFromBankToClearing(bList); result.add(batchInfo); } - } else if (ClearingMemberCategoryD.B.equals(categoryValue)) { + } else if (ClearingCategory.B.equals(categoryValue)) { batchInfo.setInitialOrder(payments); result.add(batchInfo); } else { diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ClrngValidationStored.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ClrngValidationStored.java new file mode 100644 index 000000000..27bbb5eec --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ClrngValidationStored.java @@ -0,0 +1,6 @@ +package ru.spcex.clearing.service.validation; + +public enum ClrngValidationStored { + relation, categoryPredefined + ; +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ExecutionDepositValidationRule.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ExecutionDepositValidationRule.java new file mode 100644 index 000000000..b2eaeb6da --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ExecutionDepositValidationRule.java @@ -0,0 +1,89 @@ +package ru.spcex.clearing.service.validation; + +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.company.relation.Relation; +import ru.clearing.classes.statics.data.execution.ExecutionDeposit; +import ru.spcex.clearing.error.ClearingErrorInternal; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.AccountStatus; +import ru.spcex.platform.enumeration.ClearingCategory; +import ru.spcex.platform.enumeration.ServiceStatus; +import ru.spcex.platform.enumeration.WorkflowStatus; +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 java.util.Map; +import java.util.Optional; + +public enum ExecutionDepositValidationRule implements IValidationRule> { + ClearingIsAllowed() { + @Override + public Optional validate(ImdgValidationContext context) { + ExecutionDeposit validatedObject = context.getValidatedObject(); + Imdg relationImdg = context.obtainMap(IMDGDistributedNames.Map_Relation, Relation.class); + Relation relation = relationImdg.getSingleObjectByFieldValues(Map.of("consumerId", validatedObject.getCompanyId())); + if (relation == null || !ServiceStatus.Active.equalsByKey(relation.getServiceStatus())) { + return of(ClearingErrorInternal.ClearingNotAllowed); + } + context.storeObject(ClrngValidationStored.relation, relation); + return empty(); + } + }, + AccountIsNotBlocked() { + @Override + public Optional validate(ImdgValidationContext context) { + ExecutionDeposit validatedObject = context.getValidatedObject(); + Imdg accountImdg = context.obtainMap(IMDGDistributedNames.Map_Account, Account.class); + Relation relation = context.getStoredObject(ClrngValidationStored.relation); + Account account = accountImdg.getSingleObjectByFieldValues( + Map.of("id", validatedObject.getAccountId(), "relationId", relation.getId())); + if (account == null || !AccountStatus.ACTIVE.equalsByKey(account.getAccountStatus())) { + return of(ClearingErrorInternal.AccountNotActive); + } + return empty(); + } + }, + CompanyIsNotBlocked() { + @Override + public Optional validate(ImdgValidationContext context) { + ExecutionDeposit validatedObject = context.getValidatedObject(); + Imdg companyImdg = context.obtainMap(IMDGDistributedNames.Map_Company, Company.class); + Company company = companyImdg.getSingleObjectByFieldValues(Map.of("id", validatedObject.getCompanyId())); + if (company == null || !WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus())) { + return of(ClearingErrorInternal.CompanyNotActive); + } + return empty(); + } + }, + FinancialObligationSecurity() { + @Override + public Optional validate(ImdgValidationContext context) { + ClearingCategory category = context.getStoredObject(ClrngValidationStored.categoryPredefined); + if (!category.equals(ClearingCategory.V)) { + return Optional.empty(); + } + ExecutionDeposit validatedObject = context.getValidatedObject(); + Imdg accountBalanceImdg = context.obtainMap(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class); + AccountBalance accountBalance = accountBalanceImdg.getSingleObjectByFieldValues( + Map.of("accountId", validatedObject.getAccountId(), + "companyId", validatedObject.getCompanyId())); + if (accountBalance == null) { + return of(ClearingErrorInternal.FinancialObligationNotSatisfied); + } + //todo ABS + if (validatedObject.getFirstLegAmount().compareTo(accountBalance.getFreeBalanceAmount()) > 0) { + return of(ClearingErrorInternal.FinancialObligationNotSatisfied); + } + return empty(); + } + }; + + @Override + public String ruleName() { + return "Sdf09ValidationRule." + name(); + } +} \ No newline at end of file diff --git a/clearing-parent/clearing-service/src/main/resources/application.properties b/clearing-parent/clearing-service/src/main/resources/application.properties index e0908f94a..f80985c62 100644 --- a/clearing-parent/clearing-service/src/main/resources/application.properties +++ b/clearing-parent/clearing-service/src/main/resources/application.properties @@ -20,4 +20,3 @@ clearing-service.kafka-producer.linger-ms=1 clearing-service.kafka-producer.buffer-memory=33554432 clearing-service.scheduler.check-payment-instruction=*/5 * * * * * -clearing-service.scheduler.check-s-trade=0 1 0 1 * ? diff --git a/clearing-parent/clearing-service/src/main/resources/logback.xml b/clearing-parent/clearing-service/src/main/resources/logback.xml index f96f82942..e2702c011 100644 --- a/clearing-parent/clearing-service/src/main/resources/logback.xml +++ b/clearing-parent/clearing-service/src/main/resources/logback.xml @@ -34,4 +34,9 @@ + + + + + \ No newline at end of file diff --git a/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/service/builder/PaymentInstructionCreatorTest.java b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/service/builder/PaymentInstructionCreatorTest.java new file mode 100644 index 000000000..3013ee2d9 --- /dev/null +++ b/clearing-parent/clearing-service/src/test/java/ru/spcex/clearing/service/builder/PaymentInstructionCreatorTest.java @@ -0,0 +1,29 @@ +package ru.spcex.clearing.service.builder; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; +import ru.clearing.classes.statics.data.payment.PaymentInstruction; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.time.TimeUtil; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PaymentInstructionCreatorTest { + + @Test + void nextDocumentNumber() { + ImdgProvider nullImdg = Mockito.mock(ImdgProvider.class); + PaymentInstructionCreator instance = new PaymentInstructionCreator(nullImdg); + LiabilitiesClaimsAssets liabilitiesClaimsAssets = new LiabilitiesClaimsAssets(); + liabilitiesClaimsAssets.setContract("contract"); + PaymentInstruction paymentInstruction = new PaymentInstruction(); + paymentInstruction.setPaymentDate(TimeUtil.localDateToInstant(LocalDate.of(2023,1,4))); + paymentInstruction.setSenderId(1234L); + assertEquals("contract/040123/1234/1", instance.nextDocumentNumber(liabilitiesClaimsAssets, paymentInstruction)); + assertEquals("contract/040123/1234/2", instance.nextDocumentNumber(liabilitiesClaimsAssets, paymentInstruction)); + assertEquals("contract/040123/1234/3", instance.nextDocumentNumber(liabilitiesClaimsAssets, paymentInstruction)); + } +} \ No newline at end of file diff --git a/clearing-parent/dbf-exporter/src/main/java/ru/spcex/clearing/dbf/exporter/config/KafkaSenderConfig.java b/clearing-parent/dbf-exporter/src/main/java/ru/spcex/clearing/dbf/exporter/config/KafkaSenderConfig.java index d2397c4a5..2b2319156 100644 --- a/clearing-parent/dbf-exporter/src/main/java/ru/spcex/clearing/dbf/exporter/config/KafkaSenderConfig.java +++ b/clearing-parent/dbf-exporter/src/main/java/ru/spcex/clearing/dbf/exporter/config/KafkaSenderConfig.java @@ -1,6 +1,8 @@ package ru.spcex.clearing.dbf.exporter.config; import org.apache.kafka.clients.producer.Producer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,6 +16,7 @@ import ru.spcex.platform.imdg.api.ImdgProvider; //отдельный конфиг для sender чтобы сделать required false @Configuration public class KafkaSenderConfig { + Logger log = LoggerFactory.getLogger(getClass()); private final ImdgProvider imdgProvider; private Producer kafkaProducer; @@ -23,14 +26,15 @@ public class KafkaSenderConfig { this.imdgProvider = imdgProvider; } - @Autowired + @Autowired(required = false) public void setKafkaProducer(Producer kafkaProducer) { this.kafkaProducer = kafkaProducer; } @Bean public KafkaSender kafkaSender() { - if (kafkaProducer == null ||imdgProvider == null) { + if (kafkaProducer == null || imdgProvider == null) { + log.info("Can not create KafkaSender: kafkaProducer={}, imdgProvider={}", kafkaProducer, imdgProvider); return null; } ImdgId imdgIdGenerator = imdgProvider.getImdgIdGenerator(); diff --git a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/object/ExecutionDepositMapStore.java b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/object/ExecutionDepositMapStore.java index c8a5185dd..ffd05dfb0 100644 --- a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/object/ExecutionDepositMapStore.java +++ b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/object/ExecutionDepositMapStore.java @@ -27,6 +27,10 @@ public class ExecutionDepositMapStore extends TemplateMapStore return IMDGDistributedNames.Map_ExecutionDeposit; } + public String[] getIndexingField() { + return new String[]{"exchangeExecutionId"}; + } + @Override public String[] getFields() { return new String[]{"ID", "CREATED_AT", "UPDATED_AT", diff --git a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/AbstractHazelcastLifecycleSupport.java b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/AbstractHazelcastLifecycleSupport.java index 8b9ea7df8..8b26a9649 100644 --- a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/AbstractHazelcastLifecycleSupport.java +++ b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/AbstractHazelcastLifecycleSupport.java @@ -109,6 +109,9 @@ public abstract class AbstractHazelcastLifecycleSupport implements InitializingB } } catch (InterruptedException | ExecutionException e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } throw new RuntimeException("MapStore multithreaded not complete.", e); } diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_B.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_B.java index 4d3be05d8..1f3c7ae98 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_B.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_B.java @@ -4,7 +4,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import ru.spcex.clearing.reports.reports.ReportId; import ru.spcex.clearing.reports.reports.ReportWithPeriod; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import java.time.LocalDate; import java.time.LocalDateTime; @@ -31,7 +31,7 @@ public class ReportPA_B extends ReportWithPeriod { "Отчет по нетто-позиции участника клиринга в секции МКР (Уполномоченный банк)", startDate, endDate); - this.clearingMemberCategory = ClearingMemberCategoryD.B.getKey(); + this.clearingMemberCategory = ClearingCategory.B.getKey(); } @Override diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_IV.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_IV.java index 4d733ae0c..12d124dc7 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_IV.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_12_2/ReportPA_IV.java @@ -4,7 +4,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import ru.spcex.clearing.reports.reports.ReportId; import ru.spcex.clearing.reports.reports.ReportWithPeriod; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import java.time.LocalDate; import java.time.LocalDateTime; @@ -33,8 +33,8 @@ public class ReportPA_IV extends ReportWithPeriod { startDate, endDate); this.clearingMemberCategory = "%s,%s".formatted( - ClearingMemberCategoryD.I.getKey(), - ClearingMemberCategoryD.V.getKey() + ClearingCategory.I.getKey(), + ClearingCategory.V.getKey() ); } diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_17_1/ReportBR_0420315_P1.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_17_1/ReportBR_0420315_P1.java index 70d706c99..67aafde5b 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_17_1/ReportBR_0420315_P1.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/reports/bt_17_1/ReportBR_0420315_P1.java @@ -5,7 +5,7 @@ import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import ru.spcex.clearing.reports.reports.Destination; import ru.spcex.clearing.reports.reports.ReportId; import ru.spcex.clearing.reports.reports.ReportWithClearingStatus; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.enumeration.ClearingStatus; import java.time.LocalDate; @@ -37,8 +37,8 @@ public class ReportBR_0420315_P1 extends ReportWithClearingStatus { ClearingStatus.OK.getKey()); this.destination = Destination.BR.getKey(); this.clearingMemberCategory = "%s,%s".formatted( - ClearingMemberCategoryD.I.getKey(), - ClearingMemberCategoryD.V.getKey() + ClearingCategory.I.getKey(), + ClearingCategory.V.getKey() ); } diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/AbstractReportCollector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/AbstractReportCollector.java index 6ae81d529..f6276c2cc 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/AbstractReportCollector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/AbstractReportCollector.java @@ -72,7 +72,7 @@ public abstract class AbstractReportCollector { */ protected String toString(Double val) { if (val == null) return ""; - return String.format(Locale.US, "%.2f", val.doubleValue()); + return String.format(Locale.US, "%.2f", val); } /** diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/QCommandExecutor.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/QCommandExecutor.java index 021bbc884..95436a786 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/QCommandExecutor.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/QCommandExecutor.java @@ -8,7 +8,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; -import ru.spcex.clearing.platform.messaging.domain.cud.reports.ReportWithPeriodRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; import ru.spcex.clearing.reports.config.element.ReportsServiceSettings; import ru.spcex.platform.enumeration.Task; @@ -40,20 +40,23 @@ public class QCommandExecutor extends QueueConsumer implements InitializingBean @Override public void afterPropertiesSet() { log.info("Init queue listener {}", getClass().getSimpleName()); - callback(ReportWithPeriodRequest.class) + callback(LauncherCommandRequest.class) .setConsumer(this::newReportWithPeriod) .forDestination(Task.createReport_GREP.topic(), callbacks::put); init(); } - private void newReportWithPeriod(BaseRequest reportRequest) { - ReportWithPeriodRequest request = reportRequest.getRequestPayload(); + private void newReportWithPeriod(BaseRequest reportRequest) { + LauncherCommandRequest request = reportRequest.getRequestPayload(); log.info("newReportWithPeriod request received: {}", request); + final String taskName = QUEUE_RUN_REPORT_COMMAND; //request.getTaskName(); - if (StringUtils.isBlank(request.getReportId())) - throw new IllegalStateException("ReportID is empty"); - LocalDate startDate = request.getStartDate(); - LocalDate endDate = request.getEndDate(); +// if (StringUtils.isBlank(request.getReportId())) +// throw new IllegalStateException("ReportID is empty"); +// LocalDate startDate = request.getStartDate(); +// LocalDate endDate = request.getEndDate(); + LocalDate startDate = null; + LocalDate endDate = null; if (startDate == null && endDate == null) { endDate = LocalDate.now(); startDate = LocalDate.of(endDate.getYear(), endDate.getMonth(), 1); @@ -65,18 +68,18 @@ public class QCommandExecutor extends QueueConsumer implements InitializingBean throw new IllegalStateException("StartDate > EndDate"); try { - if (QUEUE_RUN_REPORT_COMMAND.equals(request.getReportId()) - || QUEUE_RUN_DAILY_REPORT_COMMAND.equals(request.getReportId())) { - log.info("Generate all report by command {}", request.getReportId()); - reportsServiceCommand.generateReportAll(request.getReportId(), startDate, endDate); + if (QUEUE_RUN_REPORT_COMMAND.equals(taskName) + || QUEUE_RUN_DAILY_REPORT_COMMAND.equals(taskName)) { + log.info("Generate all report by command {}", taskName); + reportsServiceCommand.generateReportAll(taskName, startDate, endDate); } else { - log.info("Generate single report by command {}", request.getReportId()); - reportsServiceCommand.generateReportById(request.getReportId(), startDate, endDate); + log.info("Generate single report by command {}", taskName); + reportsServiceCommand.generateReportById(taskName, startDate, endDate); } log.debug("successfully processed"); } catch (IOException e) { - log.error("Error create report {}: {}", request.getReportId(), ExceptionUtils.getStackTrace(e)); + log.error("Error create report {}: {}", taskName, ExceptionUtils.getStackTrace(e)); } } diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportWithPeriodCollector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportWithPeriodCollector.java index e7687ed87..b11179d01 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportWithPeriodCollector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportWithPeriodCollector.java @@ -20,9 +20,9 @@ public abstract class ReportWithPeriodCollector extend LocalDate startDate, LocalDate endDate) { ImdgPredicateBuilder pb = liabilitiesClaimsAssetsImdg.predicateBuilder(); - Instant dateTimeLo = toInstantStartDay(startDate); - Instant dateTimeHi = toInstantStartDay(endDate); - ImdgPredicate p = pb.and(pb.greatEqual("refundDate", dateTimeLo), pb.lessEqual("refundDate", dateTimeHi)); +// Instant dateTimeLo = toInstantStartDay(startDate); +// Instant dateTimeHi = toInstantEndDay(endDate); + ImdgPredicate p = pb.and(pb.greatEqual("refundDate", startDate), pb.lessEqual("refundDate", endDate)); Collection liabilities = liabilitiesClaimsAssetsImdg.getCollectionObjectsByPredicate(p); return liabilities; } @@ -31,9 +31,9 @@ public abstract class ReportWithPeriodCollector extend LocalDate startDate, LocalDate endDate, Long allowClearingStatusId) { ImdgPredicateBuilder pb = liabilitiesClaimsAssetsImdg.predicateBuilder(); - Instant dateTimeLo = toInstantStartDay(startDate); - Instant dateTimeHi = toInstantStartDay(endDate); - ImdgPredicate p = pb.and(pb.greatEqual("refundDate", dateTimeLo), pb.lessEqual("refundDate", dateTimeHi), +// Instant dateTimeLo = toInstantStartDay(startDate); +// Instant dateTimeHi = toInstantEndDay(endDate); + ImdgPredicate p = pb.and(pb.greatEqual("refundDate", startDate), pb.lessEqual("refundDate", endDate), pb.equals("clearingStatus", allowClearingStatusId)); Collection liabilities = liabilitiesClaimsAssetsImdg.getCollectionObjectsByPredicate(p); return liabilities; diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportsServiceCommand.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportsServiceCommand.java index 260a368d0..499228968 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportsServiceCommand.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/ReportsServiceCommand.java @@ -12,6 +12,7 @@ import ru.spcex.clearing.reports.reports.ReportId; import ru.spcex.clearing.reports.services.builders.XMLReportBuilder; import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.utils.enumeration.IEnumKey; +import ru.spcex.platform.utils.log.ExceptionUtils; import java.io.File; import java.io.IOException; @@ -74,12 +75,20 @@ public class ReportsServiceCommand implements InitializingBean { public void generateReportAll(@Nullable String reportGroup, LocalDate startDate, LocalDate endDate) throws IOException { for (ReportWithPeriodCollector reportDataCollector : allPeriodReports) { if (reportGroup == null || QUEUE_RUN_REPORT_COMMAND.equals(reportGroup)) { - makeSingleReport(reportDataCollector, startDate, endDate); + try { + makeSingleReport(reportDataCollector, startDate, endDate); + } catch (Exception e) { + log.error("Error generate report of type {}: {}", reportDataCollector, ExceptionUtils.getStackTrace(e)); + } } } for (SimpleReportCollector reportDataCollector : allSimpleReports) { if (reportGroup == null || QUEUE_RUN_DAILY_REPORT_COMMAND.equals(reportGroup)) { - makeSingleReport(reportDataCollector); + try { + makeSingleReport(reportDataCollector); + } catch (Exception e) { + log.error("Error generate report of type {}: {}", reportDataCollector, ExceptionUtils.getStackTrace(e)); + } } } } diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420314_P2_Collector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420314_P2_Collector.java index 027135ddc..367d11525 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420314_P2_Collector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420314_P2_Collector.java @@ -45,7 +45,7 @@ public class ReportBR_0420314_P2_Collector extends ReportWithPeriodCollector searchPaymentInstruction(LocalDate startDate, LocalDate endDate) { ImdgPredicateBuilder pb = paymentInstructionImdg.predicateBuilder(); Instant dateTimeLo = toInstantStartDay(startDate); - Instant dateTimeHi = toInstantStartDay(endDate); + Instant dateTimeHi = toInstantEndDay(endDate); ImdgPredicate p = pb.and(pb.greatEqual("created", dateTimeLo), pb.lessEqual("created", dateTimeHi)); Collection paymentInstructionsAll = paymentInstructionImdg.getCollectionObjectsByPredicate(p); return paymentInstructionsAll; @@ -68,6 +68,10 @@ public class ReportBR_0420314_P2_Collector extends ReportWithPeriodCollector paymentInstructionsByCompany = entry.getValue(); Company company = companyImdg.getSingleObjectByID(companyId); + if (company == null) { + log.warn("Company not found, id={}", companyId); + continue; + } ProfileDocument profileDocumentCntr = profileDocumentImdg.getSingleObjectByFieldValues(Map.of( "companyId", companyId, "documentType", DocumentTypes.cntr.getKey() diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420317_Collector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420317_Collector.java index 8699ec969..c0d7748b0 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420317_Collector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportBR_0420317_Collector.java @@ -35,7 +35,7 @@ public class ReportBR_0420317_Collector extends ReportWithPeriodCollector searchRelation(LocalDate startDate, LocalDate endDate, String service) { ImdgPredicateBuilder pb = relationImdg.predicateBuilder(); Instant dateTimeLo = toInstantStartDay(startDate); - Instant dateTimeHi = toInstantStartDay(endDate); + Instant dateTimeHi = toInstantEndDay(endDate); ImdgPredicate p = pb.and(pb.greatEqual("updated", dateTimeLo), pb.lessEqual("updated", dateTimeHi), pb.equals("service", service)); Collection relations = relationImdg.getCollectionObjectsByPredicate(p); diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_B_Collector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_B_Collector.java index a5b84606e..bc373378d 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_B_Collector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_B_Collector.java @@ -6,7 +6,7 @@ import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.reports.reports.bt_12_2.ReportPA_B; import ru.spcex.clearing.reports.services.ReportWithPeriodCollector; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; @@ -41,7 +41,7 @@ public class ReportPA_B_Collector extends ReportWithPeriodCollector Collection clearingMemberCategories = clearingMemberCategoryImdg.getCollectionObjectsByFieldValues(Map.of( - "clearingMemberCategory", ClearingMemberCategoryD.B.getKey() + "clearingMemberCategory", ClearingCategory.B.getKey() )); Set companyIds = clearingMemberCategories.stream() .map(ClearingMemberCategory::getCompanyId) @@ -49,8 +49,8 @@ public class ReportPA_B_Collector extends ReportWithPeriodCollector ImdgPredicateBuilder builder = liabilitiesClaimsAssetsImdg.predicateBuilder(); ImdgPredicate refundDatePredicate = builder.and( - builder.greatEqual("refundDate", toInstantStartDay(startDate)), - builder.lessEqual("refundDate", toInstantStartDay(endDate)) + builder.greatEqual("refundDate", (startDate)), + builder.lessEqual("refundDate", (endDate)) ); ImdgPredicate companyIdPredicates = builder.in("companyId", companyIds.toArray(new Comparable[0])); ImdgPredicate finalPredicate = builder.and(refundDatePredicate, companyIdPredicates); diff --git a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_IV_Collector.java b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_IV_Collector.java index 627508028..159b72c32 100644 --- a/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_IV_Collector.java +++ b/clearing-parent/reports-service/src/main/java/ru/spcex/clearing/reports/services/collector/ReportPA_IV_Collector.java @@ -7,7 +7,7 @@ import ru.clearing.platform.dictionary.ClearingStatusDictionary; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.reports.reports.bt_12_2.ReportPA_IV; import ru.spcex.clearing.reports.services.ReportWithPeriodCollector; -import ru.spcex.platform.enumeration.ClearingMemberCategoryD; +import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; @@ -45,7 +45,7 @@ public class ReportPA_IV_Collector extends ReportWithPeriodCollector clearingMemberCategories = clearingMemberCategoryImdg.getCollectionObjectsBySQL(clearingMemberCategorySql); @@ -55,8 +55,8 @@ public class ReportPA_IV_Collector extends ReportWithPeriodCollector makeCmdRequest(Task byTask, Long userId) { Object toRequest; - if (Task.createReport_GREP.equals(byTask)) { // см. ru.spcex.clearing.backendapi.controller.request.cud.schedule.LauncherNew#toRequest - ReportWithPeriodRequest reportCommand = new ReportWithPeriodRequest(); - reportCommand.setReportId(byTask.getKey()); - toRequest = reportCommand; - } else { - LauncherCommandRequest taskRunnerCommandRequest = new LauncherCommandRequest(); - taskRunnerCommandRequest.setTaskName(byTask.getKey()); - taskRunnerCommandRequest.setUserId(userId); - toRequest = taskRunnerCommandRequest; - } + LauncherCommandRequest taskRunnerCommandRequest = new LauncherCommandRequest(); + taskRunnerCommandRequest.setTaskName(byTask.getKey()); + taskRunnerCommandRequest.setUserId(userId); + toRequest = taskRunnerCommandRequest; BaseRequest request = new BaseRequest<>(); request.setId(idGenerator.nextId()); request.setActionType(ActionType.NEW); diff --git a/clearing-parent/scheduler-service/src/main/java/ru/spcex/clearing/scheduler/service/LauncherService.java b/clearing-parent/scheduler-service/src/main/java/ru/spcex/clearing/scheduler/service/LauncherService.java index a41698909..aa869d0d4 100644 --- a/clearing-parent/scheduler-service/src/main/java/ru/spcex/clearing/scheduler/service/LauncherService.java +++ b/clearing-parent/scheduler-service/src/main/java/ru/spcex/clearing/scheduler/service/LauncherService.java @@ -2,10 +2,12 @@ package ru.spcex.clearing.scheduler.service; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; import ru.clearing.classes.statics.data.scheduler.Launcher; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; @@ -20,14 +22,17 @@ import java.time.Instant; import static ru.spcex.platform.utils.enumeration.IEnumKey.getEnumByKey; +@Service public class LauncherService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); private final Imdg launcherMap; + private final Producer kafkaProducer; @Autowired public LauncherService(Consumer kafkaQueue, Producer kafkaProducer, ImdgProvider imdgProvider) { super(kafkaQueue, kafkaProducer); + this.kafkaProducer = kafkaProducer; this.launcherMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Launcher, Launcher.class); } @@ -53,6 +58,7 @@ public class LauncherService extends QueueConsumer implements InitializingBean { launcher.setUpdated(created); launcherMap.insert(launcher); log.debug("successfully processed, new id {}", launcher.getId()); + kafkaProducer.send(new ProducerRecord<>("launcher-" + launcher.getTask(), userRequest)); } } diff --git a/clearing-parent/securities-service/src/main/java/ru/spcex/clearing/securities/service/cud/MoneyMarketSecurityService.java b/clearing-parent/securities-service/src/main/java/ru/spcex/clearing/securities/service/cud/MoneyMarketSecurityService.java index 37b92222a..ccf129c68 100644 --- a/clearing-parent/securities-service/src/main/java/ru/spcex/clearing/securities/service/cud/MoneyMarketSecurityService.java +++ b/clearing-parent/securities-service/src/main/java/ru/spcex/clearing/securities/service/cud/MoneyMarketSecurityService.java @@ -187,7 +187,6 @@ public class MoneyMarketSecurityService extends QueueConsumer implements Initial } MoneyMarketSecurity mms = validator.getStored(Stored.PresentById); mms.setWorkflowStatus(ru.spcex.platform.enumeration.Status.Blocked.getKey()); - Instant.now(); mms.setUpdated(Instant.now()); moneyMarketSecurityMap.update(mms); Listing listing = listingImdg.getSingleObjectByFieldValues(Map.of("securityId", mms.getId())); 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..51184296e 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"), Corr("CORR"), Anlt("ANLT"); private final String key; diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/InOutDirection.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/InOutDirection.java index ec91a9dbf..7cb6e0665 100644 --- a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/InOutDirection.java +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/InOutDirection.java @@ -3,7 +3,7 @@ package ru.spcex.platform.enumeration; import ru.spcex.platform.utils.enumeration.IEnumKey; public enum InOutDirection implements IEnumKey { - in("IN"); + in("IN"), out("OUT"); private final String key; diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ClearingMemberCategoryD.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/MoneyFlowSide.java similarity index 64% rename from platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ClearingMemberCategoryD.java rename to platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/MoneyFlowSide.java index 883769de0..1b1111295 100644 --- a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ClearingMemberCategoryD.java +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/MoneyFlowSide.java @@ -2,12 +2,14 @@ package ru.spcex.platform.enumeration; import ru.spcex.platform.utils.enumeration.IEnumKey; -public enum ClearingMemberCategoryD implements IEnumKey { - I("I"), V("V"), B("B"); +public enum MoneyFlowSide implements IEnumKey { + BUY("BUY"), + SELL("SELL"), + ; private final String key; - ClearingMemberCategoryD(String key) { + MoneyFlowSide(String key) { this.key = key; } diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ServiceStatus.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ServiceStatus.java new file mode 100644 index 000000000..000fe9999 --- /dev/null +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/ServiceStatus.java @@ -0,0 +1,23 @@ +package ru.spcex.platform.enumeration; + +import ru.spcex.platform.utils.enumeration.IEnumKey; + +public enum ServiceStatus implements IEnumKey { + Active("ACTV"), + Blocked("BLKD"), + Suspended("SSPD"), + Closed("CLOS"), + Reopened("ROPN"), +; + + private final String key; + + ServiceStatus(String key) { + this.key = key; + } + + @Override + public String getKey() { + return key; + } +} diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java index 1ee90ea9c..c515841f7 100644 --- a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java @@ -262,6 +262,7 @@ public abstract class HazelcastServiceBase lock.wait(); } } catch (InterruptedException e) { + Thread.currentThread().interrupt(); log.error(ExceptionUtils.getStackTrace(e)); } } diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/util/HazelcastHelper.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/util/HazelcastHelper.java index 3b5579484..5898d2a40 100644 --- a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/util/HazelcastHelper.java +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/util/HazelcastHelper.java @@ -84,12 +84,14 @@ public final class HazelcastHelper { log.info("Hazelcast: TextErrorService done"); done = true; } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new RuntimeException("Thread interrupted when waiting storage ready.", e); } catch (Exception e) { log.info(String.format("Waiting Hazelcast: %s -> %s", e.getClass().getSimpleName(), e.getMessage())); try { Thread.sleep(100); } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); throw new RuntimeException("Thread interrupted.", e); } } 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 b4132fa0a..97ed7998a 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,8 @@ 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 CONTINUE_CLEARING = "continue-clearing"; String LAUNCHER_NEW = "launcher-new"; String NOTIFICATION_NEW = "notification-new"; @@ -57,6 +59,7 @@ public interface Consts { String REGISTRY_COVERED_DEAL_REGISTER_NEW = "registry-covered-deal-register-new"; String REGISTRY_ADMITTED_DEAL_REGISTER_NEW = "registry-admitted-deal-register-new"; + String REGISTRY_DEAL_REGISTER_NEW = "registry-deal-register-new"; String REGISTRY_ORDER_REGISTER_NEW = "registry-order-register-new"; String REGISTRY_CONTRACT_REGISTER_NEW = "registry-contract-register-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 diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonIdRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonIdRequest.java new file mode 100644 index 000000000..57c9ea6fd --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/common/CommonIdRequest.java @@ -0,0 +1,17 @@ +package ru.spcex.clearing.platform.messaging.domain.cud.common; + +import com.fasterxml.jackson.annotation.JsonProperty; +import ru.spcex.platform.classes.base.interfaces.WithId; + +public class CommonIdRequest implements WithId { + @JsonProperty + public Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/registry/DealRegisterNewRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/registry/DealRegisterNewRequest.java new file mode 100644 index 000000000..0e37c8711 --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/registry/DealRegisterNewRequest.java @@ -0,0 +1,35 @@ +package ru.spcex.clearing.platform.messaging.domain.cud.registry; + + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDate; + +public class DealRegisterNewRequest { + + @JsonProperty + public Long executionId; + + @JsonProperty + public Long exchangeExecutionId; + + + public Long getExecutionId() { + return executionId; + } + + public void setExecutionId(Long executionId) { + this.executionId = executionId; + } + + public Long getExchangeExecutionId() { + return exchangeExecutionId; + } + + public void setExchangeExecutionId(Long exchangeExecutionId) { + this.exchangeExecutionId = exchangeExecutionId; + } + +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/schedule/LauncherCommandRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/schedule/LauncherCommandRequest.java index 4382eecb7..b05686017 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/schedule/LauncherCommandRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/schedule/LauncherCommandRequest.java @@ -6,9 +6,12 @@ public class LauncherCommandRequest { @JsonProperty private Long userId; - @JsonProperty private String taskName; + @JsonProperty + private Long companyId; + @JsonProperty + private Long securityId; public Long getUserId() { @@ -26,4 +29,20 @@ public class LauncherCommandRequest { public void setTaskName(String taskName) { this.taskName = taskName; } + + public Long getCompanyId() { + return companyId; + } + + public void setCompanyId(Long companyId) { + this.companyId = companyId; + } + + public Long getSecurityId() { + return securityId; + } + + public void setSecurityId(Long securityId) { + this.securityId = securityId; + } } diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java index 5f997b68a..ad10ca627 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/QueueConsumer.java @@ -128,6 +128,9 @@ public class QueueConsumer implements AutoCloseable { send = producer.send(new ProducerRecord<>(Consts.REQUEST_INFO_UPDATE, req)); send.get(); } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } log.error(ExceptionUtils.getStackTrace(e)); } }); diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/sender/KafkaSender.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/sender/KafkaSender.java index 8bb67b5ec..6b321bc90 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/sender/KafkaSender.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/service/sender/KafkaSender.java @@ -45,6 +45,9 @@ public class KafkaSender { try { send.get(); } catch (InterruptedException | ExecutionException e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } log.error(ExceptionUtils.getStackTrace(e)); return null; } diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/collection/Pair.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/collection/Pair.java new file mode 100644 index 000000000..3fb512341 --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/collection/Pair.java @@ -0,0 +1,27 @@ +package ru.spcex.platform.utils.collection; + +public class Pair { + private T1 first; + private T2 second; + + public Pair(T1 first, T2 second) { + this.first = first; + this.second = second; + } + + public T1 getFirst() { + return first; + } + + public void setFirst(T1 first) { + this.first = first; + } + + public T2 getSecond() { + return second; + } + + public void setSecond(T2 second) { + this.second = second; + } +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java index 10859b6a4..f2a161277 100644 --- a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java @@ -61,4 +61,12 @@ public interface IEnumId extends Serializable { default boolean equalsById(Long id) { return id != null && getId().equals(id); } + + default String nameOrId() { + try { + return ((Enum) this).name(); + } catch (ClassCastException e) { + return getId().toString(); + } + } } diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java index b30f30f12..c458709f9 100644 --- a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/number/BigDecimalUtil.java @@ -17,4 +17,9 @@ public class BigDecimalUtil { return null; } } + + public static BigDecimal safeBD(BigDecimal bd) { + return bd == null ? BigDecimal.ZERO : bd; + } + } diff --git a/pom.xml b/pom.xml index 3474338bf..6a001e386 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ ${folder_root_clearing}/clearing-parent/account-service ${folder_root_clearing}/clearing-parent/balance-service ${folder_root_clearing}/clearing-parent/company-service + ${folder_root_clearing}/clearing-parent/clearing-service ${folder_root_clearing}/clearing-parent/reports-service ${folder_root_clearing}/clearing-parent/utility-service ${folder_root_clearing}/clearing-parent/scheduler-service diff --git a/z-distr/pom.xml b/z-distr/pom.xml index 0a1d961e8..2414ad7fb 100644 --- a/z-distr/pom.xml +++ b/z-distr/pom.xml @@ -248,6 +248,25 @@ + + copy-clearing-service-bin + prepare-package + + copy + + + + + ${folder_root_clearing-service}/target/clearing-service.jar + ${folder.clearing.distr.modules}/clearing-service/clearing-service.jar + + + ${folder_root_clearing-service}/src/main/resources/application.properties + ${folder.clearing.distr.modules}/clearing-service/application.properties + + + + copy-scheduler-service-bin prepare-package