обработка SDF06 клиринг сервисом

This commit is contained in:
ialbert 2023-06-16 18:47:32 +03:00
parent 869315ce5a
commit 654b4f643c
10 changed files with 324 additions and 5 deletions

View file

@ -16,13 +16,15 @@ public class SdfExecutorsConfig {
Sdf57Executor sdf57Executor,
Sdf04Executor sdf04Executor,
Sdf13Executor sdf13Executor,
Sdf08Executor sdf08Executor) {
Sdf08Executor sdf08Executor,
Sdf06Executor sdf06Executor) {
Map<SdfTable, AbstractExecutor<?>> executors = new HashMap<>();
executors.put(SdfTable.SDF_01, sdf01Executor);
executors.put(SdfTable.SDF_57, sdf57Executor);
executors.put(SdfTable.SDF_04, sdf04Executor);
executors.put(SdfTable.SDF_13, sdf13Executor);
executors.put(SdfTable.SDF_08, sdf08Executor);
executors.put(SdfTable.SDF_06, sdf06Executor);
return executors;
}
}

View file

@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration;
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.CompanySymbols;
import ru.clearing.classes.statics.data.company.relation.Relation;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.clearing.classes.statics.data.misc.STrades;
@ -12,14 +13,18 @@ import ru.clearing.classes.statics.data.misc.Session;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.sdf.SDf01;
import ru.clearing.classes.statics.data.sdf.SDf06;
import ru.clearing.classes.statics.data.sdf.SDf08;
import ru.clearing.classes.statics.data.sdf.SDf57;
import ru.clearing.classes.statics.data.security.MoneyMarketSecurity;
import ru.clearing.classes.statics.data.security.Security;
import ru.clearing.platform.dictionary.SectionDictionary;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.service.validation.*;
import ru.spcex.clearing.util.security.UserRoleVerification;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.enumeration.UserRole;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
@ -34,6 +39,8 @@ public class ValidationConfig {
Imdg<Relation> imdgRelation;
Imdg<Company> imdgCompany;
Imdg<CompanySymbols> imdgCompanySymbols;
Imdg<Registry> imdgRegistry;
Imdg<Account> imdgAccount;
Imdg<AccountBalance> imdgAccountBalance;
Imdg<Security> imdgSecurity;
@ -41,10 +48,13 @@ public class ValidationConfig {
Imdg<TradingClearingRegistry> imdgTradingClearingRegistry;
Imdg<Session> imdgSession;
Imdg<SectionDictionary> imdgSectionDictionary;
ImdgProvider imdgProvider;
public ValidationConfig(ImdgProvider imdgProvider) {
this.imdgRelation = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class);
this.imdgCompany = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
this.imdgCompanySymbols = imdgProvider.getImdg(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class);
this.imdgRegistry = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.imdgAccount = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class);
this.imdgAccountBalance = imdgProvider.getImdg(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class);
this.imdgSecurity = imdgProvider.getImdg(IMDGDistributedNames.Map_Security, Security.class);
@ -52,6 +62,14 @@ public class ValidationConfig {
this.imdgSession = imdgProvider.getImdg(IMDGDistributedNames.Map_Session, Session.class);
this.imdgSectionDictionary = imdgProvider.getImdg(IMDGDistributedNames.Map_SectionDictionary, SectionDictionary.class);
this.imdgMoneyMarketSecurity = imdgProvider.getImdg(IMDGDistributedNames.Map_MoneyMarketSecurity, MoneyMarketSecurity.class);
this.imdgProvider = imdgProvider;
}
@Bean("adminRoleVerification")
public UserRoleVerification adminRoleVerification() {
return new UserRoleVerification(
imdgProvider, null, UserRole.Admin, ClearingError.GeneralError //fixme no UserVerifyDenial error
);
}
@Bean("executionDepositValidator")
@ -185,4 +203,22 @@ public class ValidationConfig {
);
};
}
@Bean("sdf06ValidatorNew")
public Function<SDf06, IValidator> sdf06ValidatorNew() {
return sDf06 -> {
ImdgValidationContext<SDf06> context = new ImdgValidationContext<>();
context.setValidatedObject(sDf06);
context.addImdg(IMDGDistributedNames.Map_Account, imdgAccount);
context.addImdg(IMDGDistributedNames.Map_CompanySymbols, imdgCompany);
context.addImdg(IMDGDistributedNames.Map_Registry, imdgCompany);
return new ValidatorImpl<>(context,
Sdf06NewValidationRule.CompanySymbolPresent,
Sdf06NewValidationRule.AccountPresent,
Sdf06NewValidationRule.MarketIsMkr
// Sdf06NewValidationRule.Balance
);
};
}
}

View file

@ -24,6 +24,9 @@ public enum ClearingError implements IErrorEnumId {
CurrencyNotFound(5213L),
OperationCanBeRunForTodayOnly(5214L),
AccountWrongType(5216L),
AccountNotFoundB(5217L),
MarketIsNotMkr(5217L),
BalanceInsufficient(5222L),
;
private final Long id;

View file

@ -62,12 +62,32 @@ public class StatementService extends QueueConsumer implements InitializingBean
@Override
public void afterPropertiesSet() throws Exception {
callback(StatementRequest.class)
.setConsumer(this::process)
.setConsumer(systemRequest -> {
SdfTable table = systemRequest.getRequestPayload().getTable();
if (SdfTable.SDF_06.equals(table)) {
processSdf06(systemRequest);
} else {
processPaired(systemRequest);
}
})
.forDestination(Consts.STATEMENT_PROCESS, callbacks::put);
init();
}
private void process(BaseRequest<StatementRequest> systemRequest) {
@SuppressWarnings("unchecked")
private void processSdf06(BaseRequest<StatementRequest> systemRequest) {
Imdg<SDf06> sdfImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf06, SDf06.class);
Collection<SDf06> sdf06Group = sdfImdg.getCollectionObjectsByFieldValues(Map.of(
"generationId", systemRequest.getRequestPayload().getGroupId()
));
AbstractExecutor<SDf06> service = (AbstractExecutor<SDf06>) executorsMap.get(SdfTable.SDF_06);
Result res = service.execute(sdf06Group, systemRequest.getRequestPayload());
if (service.isNeedToSendCommand()) {
service.sendCommand(kafkaSender, res);
}
}
private void processPaired(BaseRequest<StatementRequest> systemRequest) {
StatementRequest statementRequest = systemRequest.getRequestPayload();
SdfTable table = statementRequest.getTable();
log.debug("Receiving StatementRequest id={}; table {}", systemRequest.getId(), table);

View file

@ -0,0 +1,152 @@
package ru.spcex.clearing.service.executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.account.DepoAccount;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.instrument.issue.EquitySecurity;
import ru.clearing.classes.statics.data.instrument.issue.FixedIncomeSecurity;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.sdf.SDf06;
import ru.clearing.classes.statics.data.sdf.SDf09;
import ru.clearing.classes.statics.data.statement.Statement;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.Consts;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.clearing.AssetOperationRequest;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.service.model.Result;
import ru.spcex.clearing.service.validation.ValidationStored;
import ru.spcex.platform.enumeration.*;
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 ru.spcex.platform.utils.validation.IValidator;
import java.time.Instant;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
@Service
public class Sdf06Executor extends AbstractExecutor<SDf06> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Imdg<Statement> statementImdg;
private final ImdgProvider imdgProvider;
private final Imdg<Registry> registryImdg;
private final Imdg<FixedIncomeSecurity> fixedIncomeSecurityImdg;
private final Imdg<EquitySecurity> equitySecurityImdg;
private final Imdg<DepoAccount> depoAccountImdg;
private final Imdg<TradingClearingRegistry> tradingClearingRegistryImdg;
private final Imdg<Company> companyImdg;
private final Imdg<SDf09> sdf09Imdg;
private final IMessageResolver messageResolver;
private final Function<SDf06, IValidator> sDf06Validator;
private final KafkaSender kafkaSender;
//@Qualifier("sdf08ValidatorNew") Function<SDf08, IValidator> sDf08Validator
public Sdf06Executor(ImdgProvider imdgProvider,
IMessageResolver messageResolver,
@Qualifier("sdf06ValidatorNew") Function<SDf06, IValidator> sDf06Validator, KafkaSender kafkaSender) {
this.imdgProvider = imdgProvider;
this.statementImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Statement, Statement.class);
this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.fixedIncomeSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_FixedIncomeSecurity, FixedIncomeSecurity.class);
this.equitySecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_EquitySecurity, EquitySecurity.class);
this.depoAccountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_DepoAccount, DepoAccount.class);
this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class);
this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
this.sdf09Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf09, SDf09.class);
this.messageResolver = messageResolver;
this.sDf06Validator = sDf06Validator;
this.kafkaSender = kafkaSender;
}
@Override
public String exportTableName() {
return "DF-06";
}
@Override
public boolean isNeedToSendCommand() {
return false;
}
@Override
public void sendCommand(KafkaSender kafkaSender, Result result) {
}
public Result execute(Collection<SDf06> sdfs, StatementRequest statementRequest) {
Result result = new Result();
Long generationIdForGroup = imdgProvider.getImdgIdGenerator().nextId();
result.setGenerationId(generationIdForGroup);
Collection<AssetOperationRequest> requests = new ArrayList<>();
for (SDf06 sDf06 : sdfs) {
IValidator validator = sDf06Validator.apply(sDf06);
Optional<EnumMessage> err = validator.tillFirstError();
if (err.isPresent()) {
//todo если ошибка про нехватку баланса - делаем statement с ошибочным статусом
//todo 2 делаем ошибочный SDF07
log.debug("Validation error: {}", messageResolver.resolve(err.get()));
continue;
}
Long companyId = validator.getStored(ValidationStored.Sdf06CompanyId);
Account account = validator.getStored(ValidationStored.Sdf06Account);
Company company = companyImdg.getSingleObjectByID(companyId);
TradingClearingRegistry tcr = tradingClearingRegistryImdg.getSingleObjectByFieldValues(
Map.of("moneyAccountId", account.getId())
);
if (company == null || tcr == null) {
log.error("Company.id {} {}, TCR by account.id {} {} ", companyId,
company != null ? "present" : "not found",
account.getId(),
tcr != null ? "present" : "not found");
continue;
}
//проверка существует ли statement пока убрал
Statement stmt = createStatementBySdf06(sDf06, companyId, account);
statementImdg.insert(stmt);
log.debug("Statement created: {}", stmt.getId());
requests.add(requestFromStatement(stmt, company.getTradingCode(), tcr.getCode()));
}
requests.forEach(r -> kafkaSender.sendRequestToQueue(Consts.ASSET_OPERATION, r));
return result;
}
private Statement createStatementBySdf06(SDf06 sdf06, Long companyId, Account account) {
Statement stmt = new Statement();
stmt.setAddresseeId(companyId);
stmt.setSenderId(Sender.Prc.getId());
stmt.setStatementType(StatementType.incr.getKey());
stmt.setComment(sdf06.getSpec());
stmt.setAccountId(account.getId());
stmt.setAccount(account.getAccount());
stmt.setInOutDirection(InOutDirection.in.getKey());
stmt.setAmount(sdf06.getSum());
stmt.setOperationStatus(OperationStatus.Pending.getKey());
stmt.setInSDfId(sdf06.getId());
stmt.setInOutSDfType(InOutSDfType.type6.getKey());
stmt.setClearingDate(LocalDate.now());
stmt.setCreated(Instant.now());
return stmt;
}
private AssetOperationRequest requestFromStatement(Statement stmt, String tradingCode, String tcrCode) {
AssetOperationRequest req = new AssetOperationRequest();
req.setAmount(stmt.getAmount());
req.setSecuritySymbol(CurrencyCode.RUB.getKey()); //fixme retrieve security symbol from validator
req.setTradingCode(tradingCode);
req.setCode(tcrCode);
return req;
}
}

View file

@ -0,0 +1,102 @@
package ru.spcex.clearing.service.validation;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.company.CompanySymbols;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.sdf.SDf06;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.enumeration.AccountType;
import ru.spcex.platform.enumeration.CompanySymbol;
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.number.BigDecimalUtil;
import ru.spcex.platform.utils.text.TextUtil;
import ru.spcex.platform.utils.validation.IValidationRule;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;
public enum Sdf06NewValidationRule implements IValidationRule<ImdgValidationContext<SDf06>> {
CompanySymbolPresent() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<SDf06> context) {
SDf06 sdf06 = context.getValidatedObject();
Imdg<CompanySymbols> companySymbolsImdg = context.obtainMap(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class);
CompanySymbols companySymbols = null;
if (sdf06.getInn() != null) { //TextUtil.isEmpty
companySymbols = companySymbolsImdg.getSingleObjectByFieldValues(Map.of(
"companySymbol", CompanySymbol.INN.getKey(),
"companySymbolValue", sdf06.getInn()));
} else if (sdf06.getBic() != null) {
companySymbols = companySymbolsImdg.getSingleObjectByFieldValues(Map.of(
"companySymbol", CompanySymbol.BIC.getKey(),
"companySymbolValue", sdf06.getBic()));
}
if (companySymbols == null) {
return of(ClearingError.CompanyNotFoundB, "inn=" + sdf06.getInn() + "/bic=" + sdf06.getBic());
}
context.storeObject(ValidationStored.Sdf06CompanyId, companySymbols.getCompanyId());
return empty();
}
},
//// 1.2. Проверить, есть ли в таблице account счет, у которого account.account=sDf06.account
// и account.accountType=CLRN (см. справочник accountType). Иначе записать в лог ошибку (5217) "Счет %s не найден".
AccountPresent() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<SDf06> context) {
SDf06 sdf06 = context.getValidatedObject();
if (TextUtil.isEmpty(sdf06.getAccount())) {
return of(ClearingError.AccountNotFoundB, sdf06.getAccount());
}
Imdg<Account> accountImdg = context.obtainMap(IMDGDistributedNames.Map_Account, Account.class);
Account account = accountImdg.getSingleObjectByFieldValues(Map.of(
"account", sdf06.getAccount(),
"accountType", AccountType.Clrn.getKey()));
if (account == null) {
return of(ClearingError.AccountNotFoundB, sdf06.getAccount());
}
context.storeObject(ValidationStored.Sdf06Account, account);
return empty();
}
},
MarketIsMkr() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<SDf06> context) {
SDf06 sdf06 = context.getValidatedObject();
if (sdf06.getMarket() == null || !sdf06.getMarket().equals("U")) {
return of(ClearingError.MarketIsNotMkr);
}
return empty();
}
},
Balance() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<SDf06> context) {
SDf06 sdf06 = context.getValidatedObject();
if (BigDecimalUtil.safeBD(sdf06.getSum()).compareTo(BigDecimal.ZERO) >= 0) {
return empty();
}
Imdg<Registry> registryImdg = context.obtainMap(IMDGDistributedNames.Map_Registry, Registry.class);
Registry rgs = registryImdg.getSingleObjectByFieldValues(Map.of(
"account", sdf06.getAccount(),
"accountType", AccountType.Clrn.getKey()));
if (rgs == null) {
return of(ClearingError.BalanceInsufficient, sdf06.getAccount());
}
//fixme getFreeBalance???
if (BigDecimalUtil.safeBD(rgs.getCheckBalance()).compareTo(BigDecimalUtil.safeBD(sdf06.getSum()).abs()) < 0) {
return of(ClearingError.BalanceInsufficient, sdf06.getAccount());
}
return empty();
}
}
;
@Override
public String ruleName() {
return "Sdf08NewValidationRule." + name();
}
}

View file

@ -8,5 +8,7 @@ public enum ValidationStored {
Sdf01Company, Sdf01Account,
Sdf08Company, Sdf08Account
Sdf08Company, Sdf08Account,
Sdf06CompanyId, Sdf06Account
}

View file

@ -30,6 +30,7 @@ public class DbfImportKafkaMessenger implements InitializingBean {
@Override
public void afterPropertiesSet() {
messengers.put(ETable.DF_01, groupId -> messageBalance(groupId, SdfTable.SDF_01));
messengers.put(ETable.DF_06, groupId -> messageBalance(groupId, SdfTable.SDF_06));
messengers.put(ETable.DF_09, groupId -> messageBalance(groupId, SdfTable.SDF_09));
messengers.put(ETable.DF_16, groupId -> messageBalance(groupId, SdfTable.SDF_16));
messengers.put(ETable.DF_57, groupId -> messageBalance(groupId, SdfTable.SDF_57));

View file

@ -3,7 +3,7 @@ package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum InOutSDfType implements IEnumKey {
type1("0102"), type16("1617"), type9("0910"), type57("57"), type08("0809");
type1("0102"), type6("0607"), type16("1617"), type9("0910"), type57("57"), type08("0809");
private final String key;

View file

@ -5,6 +5,7 @@ import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum SdfTable implements IEnumKey {
SDF_01("SDF_01"),
SDF_04("SDF_04"),
SDF_06("SDF_06"),
SDF_08("SDF_08"),
SDF_09("SDF_09"),
SDF_10("SDF_10"),