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 1c33fe8fa..d152890b4 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 @@ -329,9 +329,10 @@ public class ValidationConfig { context.setLogPrefix(LogPrefixId.INSTANCE); return new ValidatorImpl<>(context, new PresentById(IMDGDistributedNames.Map_Registry, ClearingError.RecordNotFound, true), - ReturnDepositValidationRule.RegistryCodeCheck, + ReturnDepositValidationRule.RgsWronCodeCheck, + ReturnDepositValidationRule.CategoryCheck, ReturnDepositValidationRule.BalanceCheck, - ReturnDepositValidationRule.DmxCheck + ReturnDepositValidationRule.Dm_Check ); }; } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java index b3ca256ca..21fabb227 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/error/ClearingError.java @@ -23,6 +23,7 @@ public enum ClearingError implements IErrorEnumId { SuchRecordAlreadyExists(5405L), TradingClearingRegistryNotFound(5418L), TradingClearingRegistryNotActive(5419L), + CategoryNotFound(5420L), ClearingUnavailableForCompany(5421L), InsecurityObligation(5422L), NewDealsNotFound(5423L), 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 5b7d00041..6bcbedd89 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 @@ -166,28 +166,34 @@ public class RegistryService { msgResolver.resolve(err.get())); return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(err.get())); } - Registry tm_t = validator.getStored(Stored.PresentById); - Registry dm_x = validator.getStored(ValidationStored.ReturnDepositDmx); - if (dm_x != null) { - dm_x.setSessionId(null); - dm_x.setSessionType(null); - dm_x.setBalance(requestPayload.getBalance()); - dm_x.setUpdated(Instant.now()); - registryImdg.update(dm_x); - log.debug("updated dm*x.id={} by RegistryReturnDepositRequest.id={}", - dm_x.getId(), + Registry _m_t = validator.getStored(Stored.PresentById); + Registry dm__ = validator.getStored(ValidationStored.ReturnDepositDm__); + if (dm__ != null) { + dm__.setSessionId(null); + dm__.setSessionType(null); + dm__.setBalance(requestPayload.getBalance()); + dm__.setUpdated(Instant.now()); + registryImdg.update(dm__); + log.debug("updated {}.id={} by RegistryReturnDepositRequest.id={}", + dm__.getRegistryCode(), + dm__.getId(), req.getId()); } else { - dm_x = tm_t.clone(); - dm_x.setRegistryDesignation(RegistryDesignation.D.getKey()); - dm_x.setRegistryUnit(RegistryUnit.X.getKey()); - dm_x.setRegistryCode(RegistryUtil.clearingCode(dm_x)); - dm_x.setBalance(requestPayload.getBalance()); - dm_x.setSessionId(null); - dm_x.setSessionType(null); - registryImdg.insert(dm_x); - log.debug("created dm*x.id={} by RegistryReturnDepositRequest.id={}", - dm_x.getId(), + dm__ = _m_t.clone(); + dm__.setRegistryDesignation(RegistryDesignation.D.getKey()); + if (RegistryDesignation.T.equalsByKey(_m_t.getRegistryDesignation())) { + dm__.setRegistryUnit(RegistryUnit.X.getKey()); + } else { + dm__.setRegistryUnit(RegistryUnit.T.getKey()); + } + dm__.setRegistryCode(RegistryUtil.clearingCode(dm__)); + dm__.setBalance(requestPayload.getBalance()); + dm__.setSessionId(null); + dm__.setSessionType(null); + registryImdg.insert(dm__); + log.debug("created {}.id={} by RegistryReturnDepositRequest.id={}", + dm__.getRegistryCode(), + dm__.getId(), req.getId()); } return new RequestInfoUpdate(req.getId(), Status.Success, null); diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ReturnDepositValidationRule.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ReturnDepositValidationRule.java index 31b1c6a28..421b59e1f 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ReturnDepositValidationRule.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ReturnDepositValidationRule.java @@ -1,23 +1,68 @@ package ru.spcex.clearing.service.validation; +import ru.clearing.classes.statics.data.company.ClearingMemberCategory; import ru.clearing.classes.statics.data.registry.Registry; import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest; -import ru.spcex.clearing.service.registry.RegistryManager; -import ru.spcex.platform.enumeration.RegistryDesignation; -import ru.spcex.platform.enumeration.RegistryInstrumentType; -import ru.spcex.platform.enumeration.RegistryStatus; -import ru.spcex.platform.enumeration.RegistryUnit; +import ru.spcex.platform.enumeration.*; import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.predicate.specific.RegistryCodeSqlBuilder; import ru.spcex.platform.imdg.validation.ImdgValidationContext; import ru.spcex.platform.imdg.validation.Stored; import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.text.TextUtil; import ru.spcex.platform.utils.validation.IValidationRule; +import java.math.BigDecimal; import java.util.Optional; +import static ru.spcex.clearing.service.registry.RegistryManager.equalsByCode; + +//1. Проверки +//1.1 Проверить, в запросе у регистра код: +// 1.1.1 Если регистр TM-T: +// Проверить, что у registry.companyId существует категория I, иначе ошибка: 5420 - "Доступ к клирингу на «%s» не найден для компании %s."/> Где %s = категории И +// Проверить, что найденного регистра DM*X: registry.status!=OK, иначе ошибка: 5425 - "Запрещена идентификация средства по рассчитанным обязательствам". +// Создать/добавить в баланс DM-X на базе TM-T +// 1.1.2 Если регистр OM-T: +// Проверить, что у registry.companyId существует категория V, иначе ошибка: 5420 - "Доступ к клирингу на «%s» не найден для компании %s."/> Где %s = категории В +// Проверить, что найденного регистра DM*T: registry.status!=OK, иначе ошибка: 5425 - "Запрещена идентификация средства по рассчитанным обязательствам". +// Создать/добавить в баланс DM-T на базе OM-T +//1.2 Проверить, баланс в запросе +// Баланс должен быть положительный public enum ReturnDepositValidationRule implements IValidationRule> { + RgsWronCodeCheck() { + @Override + public Optional validate(ImdgValidationContext context) { + Registry rgs = context.getStoredObject(Stored.PresentById); + if (!equalsByCode(RegistryTradingParams.TM_T, rgs) && !equalsByCode(RegistryTradingParams.OM_T, rgs)) { + return of(ClearingError.RgsWrongCode, "{}.id={}", rgs.getRegistryCode(), rgs.getId()); + } + return empty(); + } + }, + CategoryCheck() { + @Override + public Optional validate(ImdgValidationContext context) { + Registry rgs = context.getStoredObject(Stored.PresentById); + ClearingCategory ctgr = null; + if (equalsByCode(RegistryTradingParams.TM_T, rgs)) { + ctgr = ClearingCategory.I; + } else { + ctgr = ClearingCategory.V; + } + Imdg ctgImdg = context.obtainMap(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); + ClearingMemberCategory category = + ctgImdg.getFirstObjectBySQL("companyId = %d and clearingMemberCategory = '%s'" + .formatted(rgs.getCompanyId(), ctgr.getKey())); + if (category == null) { + return of(ClearingError.CategoryNotFound, ctgr.getKey(), rgs.getCompanyId()); + } + return empty(); + } + }, + @Deprecated RegistryCodeCheck() { @Override public Optional validate(ImdgValidationContext context) { @@ -34,23 +79,40 @@ public enum ReturnDepositValidationRule implements IValidationRule validate(ImdgValidationContext context) { Registry rgs = context.getStoredObject(Stored.PresentById); RegistryReturnDepositRequest request = context.getValidatedObject(); - if (request.getBalance() == null || request.getBalance().compareTo(rgs.getBalance()) > 0) { + if (request.getBalance() == null || request.getBalance().compareTo(BigDecimal.ZERO) < 0) { + return of(ClearingError.WrongField, "balance"); + } + if (request.getBalance().compareTo(rgs.getBalance()) > 0) { return of(ClearingError.IdentifiedFundsExceedObligations); } return Optional.empty(); } - }, DmxCheck() { + }, Dm_Check() { @Override public Optional validate(ImdgValidationContext context) { Registry rgs = context.getStoredObject(Stored.PresentById); Imdg rgsImdg = context.obtainMap(IMDGDistributedNames.Map_Registry, Registry.class); - RegistryManager rgsMng = new RegistryManager(rgsImdg); - Optional dmx = rgsMng.searchDmxByCompanyId(rgs); - if (dmx.isPresent() && RegistryStatus.OK.equalsByKey(dmx.get().getRegistryStatus()) ) { + if (TextUtil.isEmpty(rgs.getContract())) { + return empty(); + } + RegistryTradingParams rgsCode; + if (equalsByCode(RegistryTradingParams.TM_T, rgs)) { + rgsCode = RegistryTradingParams.DM_X; + } else { + rgsCode = RegistryTradingParams.DM_T; + } + String sqlCondition = String.format("(%s) and companyId = %d and contract = '%s'", + RegistryCodeSqlBuilder.getInstance(rgsCode).build(), + rgs.getCompanyId(), + rgs.getContract()); + Registry dm__ = rgsImdg.getFirstObjectBySQL(sqlCondition); + if (dm__ != null && RegistryStatus.OK.equalsByKey(dm__.getRegistryStatus()) ) { return of(ClearingError.ObligationsAlreadyCalculated); } - dmx.ifPresent(dmxRgs -> context.storeObject(ValidationStored.ReturnDepositDmx, dmxRgs)); - return Optional.empty(); + if (dm__ != null) { + context.storeObject(ValidationStored.ReturnDepositDm__, dm__); + } + return empty(); } }; diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java index 471106a79..a0ad7e8a8 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/validation/ValidationStored.java @@ -18,7 +18,7 @@ public enum ValidationStored { Sdf06Company, Sdf06Account, Sdf06Tcr, - ReturnDepositDmx, + ReturnDepositDm__, IdentificationFundsTcrSearch, IdentificationFundsAssetTrio, diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/ReturnDepositSession.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/ReturnDepositSession.java index 4eadca2cb..5913b209e 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/ReturnDepositSession.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/ReturnDepositSession.java @@ -160,6 +160,7 @@ public class ReturnDepositSession extends AbstractSession implements Initializin { InspectionPoolPayload companyIdPayload = new InspectionPoolPayload(); companyIdPayload.setProcessedCompanyId(currSession.getCompanyId()); + companyIdPayload.setSessionId(currSession.getId()); runStage(TaskType.InspectionObligations, companyIdPayload, inspectionObligationsDepositReturn); } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligations.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligations.java index 327b6d8b2..a4161430a 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligations.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligations.java @@ -1,7 +1,5 @@ package ru.spcex.clearing.session.stage.impl; -import org.apache.commons.lang3.tuple.MutableTriple; -import org.apache.commons.lang3.tuple.Triple; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -55,16 +53,18 @@ public class InspectionObligations implements ISessionStage { private final AssetTBFProcessing assets; private final GatewayRequester gateway; private final TradingTimeService tradingTimeService; + private final PlanBalanceCalc planBalanceCalc; @Autowired public InspectionObligations(ImdgProvider imdgProvider, RegistryManager registryManager, AssetTBFProcessing assets, - GatewayRequester gateway) { + GatewayRequester gateway, PlanBalanceCalc planBalanceCalc) { this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); this.registryManager = registryManager; this.assets = assets; this.gateway = gateway; + this.planBalanceCalc = planBalanceCalc; this.gateway.setName("InspectionObligations|OM*T"); this.tradingTimeService = new TradingTimeService(imdgProvider); } @@ -204,52 +204,11 @@ public class InspectionObligations implements ISessionStage { } } - stageRevision2(sessionId); + planBalanceCalc.stageRevision2(sessionId); return new StageResult(null, true); } - protected void stageRevision2(Long sessionId) { - /* todo logic - Collection rgsAT = findRegistryATBySession // registry_disignation=A and registry_unit=T -Collection rgsAB = findRegistryATBySession // registry_disignation=A and registry_unit=B and sessionId = current - -foreach( at: rgsAT) - foreach (ab: rgsAB) - if (at.companyId=ab.companyId and at.accountId=ab.accountId and at.securityId=ab.securityId) - at.plannecBallance -= ab.balance - */ - String sqlAT = RegistryCodeSqlBuilder.getInstance(A__T).build(); - String sqlAB = String.format("(%s) and sessionId = %d", - RegistryCodeSqlBuilder.getInstance(A__B).build(), - sessionId - ); - Collection registriesAT = registryImdg.getCollectionObjectsBySQL(sqlAT); - Collection registriesAB = registryImdg.getCollectionObjectsBySQL(sqlAB); - log.debug("Select {} registers by \"{}\", {} registers by \"{}\" for revision step 2", - registriesAT.size(), sqlAT, registriesAB.size(), sqlAB); - Map, List> regABIndex = registriesAB.stream().collect(Collectors.groupingBy( - (Registry reg) -> new MutableTriple(reg.getCompanyId(), reg.getAccountId(), reg.getSecurityId()) - )); - int updateCount = 0; - Instant now = Instant.now(); - for (Registry regT : registriesAT) { - Triple key = new MutableTriple(regT.getCompanyId(), regT.getAccountId(), regT.getSecurityId()); - List regsB = regABIndex.get(key); - if (regsB == null) { - log.debug("Registry A__B for registry[{}] (A__T key {}) not found", regT, key); - } else { - for (Registry regB : regsB) { - regT.setPlanBalance(safeBD(regT.getPlanBalance()).subtract(safeBD(regB.getBalance()))); - } - regT.setUpdated(now); - registryImdg.update(regT); - updateCount++; - } - } - log.debug("Updated {} registers A__T with planBalance at {}", updateCount, now); - } - private String searchAssetsByObligationSql(Registry obligation) { RegistryTradingParams counterRegistryTradingParams = null; if (IEnumKey.getEnumByKey(RegistryInstrumentType.class, obligation.getRegistryInstrumentType()) == RegistryInstrumentType.S) { diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligationsDepositReturn.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligationsDepositReturn.java index 0e9742fb9..4d6df6934 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligationsDepositReturn.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/InspectionObligationsDepositReturn.java @@ -16,6 +16,7 @@ import ru.spcex.clearing.service.schedule.TradingTimeService; import ru.spcex.clearing.session.stage.ISessionStage; import ru.spcex.clearing.session.stage.StageResult; import ru.spcex.clearing.session.stage.Task; +import ru.spcex.clearing.session.stage.task.InspectionPoolPayload; import ru.spcex.platform.enumeration.*; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; @@ -44,23 +45,26 @@ public class InspectionObligationsDepositReturn implements ISessionStage { private final AssetTBFProcessing assets; private final TradingTimeService tradingTimeService; private final GatewayRequester gateway; + private final PlanBalanceCalc planBalanceCalc; @Autowired - public InspectionObligationsDepositReturn(ImdgProvider imdgProvider, RegistryManager rgsMng, AssetTBFProcessing assets, TradingTimeService tradingTimeService, GatewayRequester gateway) { + public InspectionObligationsDepositReturn(ImdgProvider imdgProvider, RegistryManager rgsMng, AssetTBFProcessing assets, TradingTimeService tradingTimeService, GatewayRequester gateway, PlanBalanceCalc planBalanceCalc) { this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); this.categoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); this.rgsMng = rgsMng; this.assets = assets; this.tradingTimeService = tradingTimeService; this.gateway = gateway; + this.planBalanceCalc = planBalanceCalc; this.gateway.setName("InspectionObligationsDepositReturn|OM*T"); } @Override public StageResult submit(Task task) { + InspectionPoolPayload payload = (InspectionPoolPayload) task.getData(); switch (task.getTaskType()) { case InspectionObligations -> { - return inspectionObligations(); + return inspectionObligations(payload.getSessionId()); } default -> throw new IllegalStateException("Unknown task type: " + task.getTaskType()); } @@ -81,7 +85,7 @@ public class InspectionObligationsDepositReturn implements ISessionStage { } } - private StageResult inspectionObligations() { + private StageResult inspectionObligations(Long sessionId) { String sqlCondition = String.format("(%s) and registryStatus = '%s'", RegistryCodeSqlBuilder.getInstance(OS_T, OM_T, TS_T, TM_T).build(), RegistryStatus.POOL.getKey()); @@ -196,6 +200,7 @@ public class InspectionObligationsDepositReturn implements ISessionStage { group.forEach(rgs -> updateStatus(rgs, registryStatusFailed(rgs))); } } + planBalanceCalc.stageRevision2(sessionId); return new StageResult<>(null, true); } diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/PlanBalanceCalc.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/PlanBalanceCalc.java new file mode 100644 index 000000000..c1c51a30c --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/PlanBalanceCalc.java @@ -0,0 +1,73 @@ +package ru.spcex.clearing.session.stage.impl; + +import org.apache.commons.lang3.tuple.MutableTriple; +import org.apache.commons.lang3.tuple.Triple; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.predicate.specific.RegistryCodeSqlBuilder; + +import java.time.Instant; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static ru.spcex.platform.enumeration.RegistryTradingParams.A__B; +import static ru.spcex.platform.enumeration.RegistryTradingParams.A__T; +import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD; + +@Component +public class PlanBalanceCalc { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Imdg rgsImdg; + + public PlanBalanceCalc(ImdgProvider imdgProvider) { + this.rgsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); + } + + public void stageRevision2(Long sessionId) { + /* todo logic + Collection rgsAT = findRegistryATBySession // registry_disignation=A and registry_unit=T +Collection rgsAB = findRegistryATBySession // registry_disignation=A and registry_unit=B and sessionId = current + +foreach( at: rgsAT) + foreach (ab: rgsAB) + if (at.companyId=ab.companyId and at.accountId=ab.accountId and at.securityId=ab.securityId) + at.plannecBallance -= ab.balance + */ + String sqlAT = RegistryCodeSqlBuilder.getInstance(A__T).build(); + String sqlAB = String.format("(%s) and sessionId = %d", + RegistryCodeSqlBuilder.getInstance(A__B).build(), + sessionId + ); + Collection registriesAT = rgsImdg.getCollectionObjectsBySQL(sqlAT); + Collection registriesAB = rgsImdg.getCollectionObjectsBySQL(sqlAB); + log.debug("Select {} registers by \"{}\", {} registers by \"{}\" for revision step 2", + registriesAT.size(), sqlAT, registriesAB.size(), sqlAB); + Map, List> regABIndex = registriesAB.stream().collect(Collectors.groupingBy( + (Registry reg) -> new MutableTriple(reg.getCompanyId(), reg.getAccountId(), reg.getSecurityId()) + )); + int updateCount = 0; + Instant now = Instant.now(); + for (Registry regT : registriesAT) { + Triple key = new MutableTriple(regT.getCompanyId(), regT.getAccountId(), regT.getSecurityId()); + List regsB = regABIndex.get(key); + if (regsB == null) { + log.debug("Registry A__B for registry[{}] (A__T key {}) not found", regT, key); + } else { + for (Registry regB : regsB) { + regT.setPlanBalance(safeBD(regT.getPlanBalance()).subtract(safeBD(regB.getBalance()))); + } + regT.setUpdated(now); + rgsImdg.update(regT); + updateCount++; + } + } + log.debug("Updated {} registers A__T with planBalance at {}", updateCount, now); + } +}