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 index 35cf92ea5..01be1971a 100644 --- 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 @@ -25,8 +25,10 @@ import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.cud.payment.PIClearingOutbondActionNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistrySplitDepositActionRequest; import ru.spcex.clearing.service.validation.*; import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.clearing.validation.common.rules.FieldRequiredRule; import ru.spcex.clearing.validation.common.rules.IdPresentRule; import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.imdg.api.Imdg; @@ -336,6 +338,20 @@ public class ValidationConfig { }; } + @Bean("splitDepositValidator") + public Function splitDepositValidator() { + return refundDateRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(refundDateRequest); + context.addImdg(IMDGDistributedNames.Map_Registry, imdgRegistry); + context.setLogPrefix(r -> "contract=%d refundDate=%s".formatted(r.getContract(), r.getRefundDate())); + return new ValidatorImpl<>(context, + FieldRequiredRule.instance("contract", RegistrySplitDepositActionRequest::getContract, ClearingError.RequiredFieldEmpty), + FieldRequiredRule.instance("refundDate", RegistrySplitDepositActionRequest::getRefundDate, ClearingError.RequiredFieldEmpty) + ); + }; + } + @Bean public Function piOutboundValidation() { return pmtOut -> { 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 2bcac2b3d..c6344e6ce 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 @@ -17,6 +17,7 @@ import ru.spcex.clearing.platform.messaging.domain.cud.gateway.AssetOperationApp import ru.spcex.clearing.platform.messaging.domain.cud.payment.PIClearingOutbondActionNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistrySplitDepositActionRequest; import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest; import ru.spcex.clearing.platform.messaging.domain.cud.utilities.STradesImportedRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; @@ -138,6 +139,9 @@ public class EventsReceiver extends QueueConsumer implements InitializingBean { callback(LauncherCommandRequest.class) .setConsumer(event -> registryService.resetBalances()) .forDestination(Task.createRegistry_GRYT.topic(), callbacks::put); + callback(RegistrySplitDepositActionRequest.class) + .setFunction(registryService::splitDeposit) + .forDestination(Consts.REGISTRY_SPLIT_DEPOSIT_ACTION, callbacks::put); callback(LauncherCommandRequest.class) .setConsumer(task -> { diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/RegistryService.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/RegistryService.java index 1dba37a2e..be0d41112 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/RegistryService.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/RegistryService.java @@ -6,18 +6,22 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; +import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.domain.cud.clearing.CreateRegistryRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistrySplitDepositActionRequest; import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; import ru.spcex.clearing.platform.messaging.service.Status; import ru.spcex.clearing.service.validation.ValidationStored; import ru.spcex.clearing.session.stage.util.RegistryUtil; +import ru.spcex.clearing.util.security.UserRoleVerification; import ru.spcex.platform.enumeration.*; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.ImdgTransaction; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.imdg.api.predicate.specific.RegistryCodeSqlBuilder; @@ -43,18 +47,25 @@ public class RegistryService { private final Imdg registryImdg; private final Function returnDepositVal; private final Function refundDateVal; + private final Function splitDepositActionVal; + private final IMessageResolver msgResolver; + private final UserRoleVerification rights; public RegistryService(ImdgProvider imdgProvider, @Qualifier("returnDepositValidator") Function returnDepositVal, @Qualifier("refundDateValidator") Function refundDateVal, - IMessageResolver msgResolver) { + @Qualifier("splitDepositValidator") Function splitDepositActionVal, + IMessageResolver msgResolver, + UserRoleVerification rights) { this.imdgProvider = imdgProvider; this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); this.returnDepositVal = returnDepositVal; this.refundDateVal = refundDateVal; + this.splitDepositActionVal = splitDepositActionVal; this.msgResolver = msgResolver; + this.rights = rights; } public void createRegistry(CreateRegistryRequest createRegistryRequest) { @@ -276,4 +287,122 @@ public class RegistryService { registryImdg.update(registry); } } + + + public RequestInfoUpdate splitDeposit(BaseRequest req) { + RegistrySplitDepositActionRequest requestPayload = req.getRequestPayload(); + RequestInfoUpdate roleCheck = rights.validateRoleAndGetResult(req); + if (roleCheck != null) { + return roleCheck; + } + { + IValidator validator = splitDepositActionVal.apply(requestPayload); + Optional err = validator.tillFirstError(); + if (err.isPresent()) { + log.info("RegistrySplitDepositActionRequest request id={} validation error: {}", + req.getId(), + msgResolver.resolve(err.get())); + return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(err.get())); + } + } + + Registry baseRegistry; + { + ImdgPredicateBuilder prdBldr = registryImdg.predicateBuilder(); + ImdgPredicate query = prdBldr.and( + RegistryCodeSqlBuilder.getInstance(RegistryTradingParams.OM_T).buildPredicate(prdBldr), + prdBldr.equals("contract", requestPayload.getContract())); + baseRegistry = registryImdg.getSingleObjectByPredicate(query); + if (baseRegistry == null) { + String errMsg = msgResolver.resolve(new EnumMessage(ClearingError.TCRegistryNotFound, requestPayload.getContract())); + log.info("RegistrySplitDepositActionRequest request id={}, registry query \"{}\": {}", + req.getId(), query, errMsg); + return new RequestInfoUpdate(req.getId(), Status.Error, errMsg); + } else { + log.debug("Registry.id={} found by query \"{}\"", + baseRegistry.getId(), query); + } + } + { + Optional err = validateSplitDepositRegister(baseRegistry, requestPayload); + if (err.isPresent()) { + log.info("RegistrySplitDepositActionRequest request id={} cross validation with registry.id={} error: {}", + req.getId(), baseRegistry.getId(), msgResolver.resolve(err.get())); + return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(err.get())); + } + } + + ImdgTransaction txCtx = imdgProvider.newTransaction(); + boolean txOk = false; + txCtx.beginTransaction(); + try { + Imdg registryTMap = txCtx.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); + Instant now = Instant.now(); + InOutDirection direction = InOutDirection.out; // todo откуда брать направление? + // 1. Первая группа + Registry firstRegistry = baseRegistry.clone(); + firstRegistry.setId(null); + firstRegistry.setCreated(now); + firstRegistry.setUpdated(null); + if (InOutDirection.out == direction) { + firstRegistry.setBalance(baseRegistry.getBalance().subtract(requestPayload.getOutboundAmount())); + } else if (InOutDirection.in == direction) { + firstRegistry.setBalance(baseRegistry.getBalance().add(requestPayload.getOutboundAmount())); + } else { + log.warn("Direction not set, do not change Balance"); + } + firstRegistry.setContract(baseRegistry.getContract() + "~1"); + registryTMap.insert(firstRegistry); + + // 2. Вторая группа + Registry secondRegistry = null; + if (InOutDirection.out == direction) { + secondRegistry.setId(null); + secondRegistry.setCreated(now); + secondRegistry.setUpdated(null); + secondRegistry.setBalance(requestPayload.getOutboundAmount()); + secondRegistry.setContract(baseRegistry.getContract() + "~2"); + registryTMap.insert(secondRegistry); + } + + // 3. Текущим обязательствам установить статус SPLT + baseRegistry.setRegistryStatus(RegistryStatus.SPLT.getKey()); + baseRegistry.setUpdated(Instant.now()); + registryTMap.update(baseRegistry); + + txOk = true; + log.debug("Registry[{}] groupId={} split to: id1={} {}, id2={} {}", + baseRegistry.getId(), baseRegistry.getGroupId(), + firstRegistry == null ? null : firstRegistry.getId(), firstRegistry == null ? null : firstRegistry.getContract(), + secondRegistry == null ? null : secondRegistry.getId(), secondRegistry == null ? null : secondRegistry.getContract() + ); + } finally { + if (txOk) { + txCtx.commitTransaction(); + } else { + txCtx.rollbackTransaction(); + log.debug("Transaction rollback {}", txCtx); + } + } + + return new RequestInfoUpdate(req.getId(), Status.Success, null); + } + + private Optional validateSplitDepositRegister(Registry registry, RegistrySplitDepositActionRequest request) { + if (!RegistryStatus.PROC.equalsByKey(registry.getRegistryStatus())) { + log.trace("Registry[{}].RegistryStatus={} - error", registry.getId(), registry.getRegistryStatus()); + return Optional.of(new EnumMessage(ClearingError.TCRegistryNotFound, request.getContract())); + } + BigDecimal regBalance = registry.getBalance(); + if (regBalance == null || regBalance.compareTo(request.getOutboundAmount()) < 0) { + log.trace("Registry[{}] balance={} < OutboundAmount {}", registry.getId(), regBalance, request.getOutboundAmount()); + return Optional.of(new EnumMessage(ClearingError.BalanceInsufficient, request.getContract())); + } + if (!registry.getRefundDate().isAfter(request.getRefundDate())) { + log.trace("Registry[{}].RefundDate={} but request RefundDate={}", registry.getId(), registry.getRefundDate(), request.getRefundDate()); + return Optional.of(new EnumMessage(ClearingError.WrongFieldValue, "RefundDate")); + } + return Optional.empty(); + } + } diff --git a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/RegistryStatus.java b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/RegistryStatus.java index 4438e4b7e..418b1cfff 100644 --- a/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/RegistryStatus.java +++ b/platform-parent/platform-enum/src/main/java/ru/spcex/platform/enumeration/RegistryStatus.java @@ -11,7 +11,8 @@ public enum RegistryStatus implements IEnumKey { FAIL("FAIL"), UNCV("UNCV"), MNG("MNG"), - POOL("POOL") + POOL("POOL"), + SPLT("SPLT") ; private final String key;