SDF55/PaymentInstructionOutboundService

This commit is contained in:
ialbert 2023-09-18 17:40:30 +03:00
parent 1deb4337cb
commit eef8e8499a
9 changed files with 215 additions and 28 deletions

View file

@ -13,6 +13,7 @@ import ru.clearing.classes.statics.data.instrument.issue.EquitySecurity;
import ru.clearing.classes.statics.data.instrument.issue.FixedIncomeSecurity;
import ru.clearing.classes.statics.data.misc.STrades;
import ru.clearing.classes.statics.data.misc.Session;
import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.sdf.*;
@ -23,6 +24,7 @@ import ru.clearing.platform.dictionary.InOutDirectionDictionary;
import ru.clearing.platform.dictionary.SectionDictionary;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
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.RegistryChangeStatusExtractRequest;
@ -66,6 +68,9 @@ public class ValidationConfig {
Imdg<SectionDictionary> imdgSectionDictionary;
Imdg<Statement> imdgStatement;
Imdg<InOutDirectionDictionary> imdgInOutDirection;
Imdg<SDf55> sDf55Imdg;
Imdg<SDf54> sDf54Imdg;
Imdg<PaymentInstruction> pmtImdg;
ImdgProvider imdgProvider;
public ValidationConfig(ImdgProvider imdgProvider) {
@ -85,6 +90,9 @@ public class ValidationConfig {
this.imdgStatement = imdgProvider.getImdg(IMDGDistributedNames.Map_Statement, Statement.class);
this.imdgDepoAccount = imdgProvider.getImdg(IMDGDistributedNames.Map_DepoAccount, DepoAccount.class);
this.imdgInOutDirection = imdgProvider.getImdg(IMDGDistributedNames.Map_InOutDirectionDictionary, InOutDirectionDictionary.class);
this.sDf55Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf55, SDf55.class);
this.sDf54Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf54, SDf54.class);
this.pmtImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class);
this.imdgProvider = imdgProvider;
}
@ -432,6 +440,26 @@ public class ValidationConfig {
};
}
@Bean
public Function<StatementRequest, IValidator> sdf55ExecutorValidation() {
return req -> {
ImdgValidationContext<StatementRequest> ctx = new ImdgValidationContext<>();
ctx.setValidatedObject(req);
ctx.addImdg(IMDGDistributedNames.Map_Registry, imdgRegistry);
ctx.addImdg(IMDGDistributedNames.Map_SDf55, sDf55Imdg);
ctx.addImdg(IMDGDistributedNames.Map_SDf54, sDf54Imdg);
ctx.addImdg(IMDGDistributedNames.Map_PaymentInstruction, pmtImdg);
ctx.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, imdgTradingClearingRegistry);
return new ValidatorImpl<>(ctx,
Sdf55ValidationRule.Fields,
Sdf55ValidationRule.GenerationIdFound,
Sdf55ValidationRule.Sdf54Present,
Sdf55ValidationRule.PaymentInstructionPresent,
Sdf55ValidationRule.TcrPresent,
Sdf55ValidationRule.AssetsPresent);
};
}
@Bean("userRoleVerification")
public UserRoleVerification userRoleVerification(ImdgProvider imdgProvider, IMessageResolver msgs) {
return new UserRoleVerification(imdgProvider, msgs, ClearingError.UserVerifyDenial);

View file

@ -159,7 +159,7 @@ public class Sdf06Executor {
dmiService.setProc(tcr.getId(),
CurrencyCode.RUB.getKey(),
safeBD(sDf06.getSum()),
sDf06.getNumber());
sDf06.getNumber().toString());
}
sdf07WasCreated = true;
}
@ -266,7 +266,7 @@ public class Sdf06Executor {
dmiService.setProc(searchTcrOnGatewayResponse(sdf06).map(SpcexObjectBase::getId).orElse(null),
CurrencyCode.RUB.getKey(),
safeBD(sdf06.getSum()),
sdf06.getNumber()
sdf06.getNumber().toString()
);
} else {
log.trace("statement.id={}, sdf07.id={}, sdf06.id={} rejected (by gateway answer)",

View file

@ -2,18 +2,31 @@ 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.payment.PaymentInstruction;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.sdf.SDf54;
import ru.clearing.classes.statics.data.sdf.SDf55;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
import ru.spcex.clearing.service.AssetTrio;
import ru.spcex.clearing.service.registry.AssetTBFProcessing;
import ru.spcex.clearing.service.registry.DmiService;
import ru.spcex.clearing.service.validation.ValidationStored;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.TransactionStatus;
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.math.BigDecimal;
import java.time.Instant;
import java.util.Optional;
import java.util.function.Function;
@Service
public class Sdf55Executor {
@ -21,35 +34,46 @@ public class Sdf55Executor {
private final Imdg<SDf54> sDf54Imdg;
private final Imdg<SDf55> sDf55Imdg;
private final Imdg<PaymentInstruction> pmtImdg;
private final Function<StatementRequest, IValidator> validation;
private final IMessageResolver msgRslv;
private final DmiService dmiService;
private final AssetTBFProcessing assets;
public Sdf55Executor(ImdgProvider imdgProvider) {
public Sdf55Executor(ImdgProvider imdgProvider,
@Qualifier("sdf55ExecutorValidation") Function<StatementRequest, IValidator> validation, IMessageResolver msgRslv, DmiService dmiService, AssetTBFProcessing assets) {
this.sDf54Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf54, SDf54.class);
this.sDf55Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf55, SDf55.class);
this.pmtImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class);
this.validation = validation;
this.msgRslv = msgRslv;
this.dmiService = dmiService;
this.assets = assets;
}
public void execute(BaseRequest<StatementRequest> systemRequest) {
StatementRequest payload = systemRequest.getRequestPayload();
log.debug("executing {}.generationId={}", payload.getTable(), payload.getGroupId());
SDf55 sDf55 = sDf55Imdg.getFirstObjectBySQL("generationId = %d".formatted(payload.getGroupId()));
if (sDf55 == null) {
log.error("sDf55 not found for generationId={}", payload.getGroupId());
return;
}
SDf54 sDf54 = sDf54Imdg.getFirstObjectBySQL("docnm_ref = '%s'".formatted(sDf55.getDocnm_ref()));
if (sDf54 == null) {
log.error("sDf54 not found for sDf55.id={} docnm_ref={}", sDf55.getId(), sDf55.getDocnm_ref());
return;
}
PaymentInstruction pmt = pmtImdg.getSingleObjectByID(sDf54.getGenerationId());
if (pmt == null) {
log.error("pmt not found for sDf54.id={} generationId={}", sDf54.getId(), sDf54.getGenerationId());
IValidator validator = validation.apply(payload);
Optional<EnumMessage> err = validator.tillFirstError();
if (err.isPresent()) {
log.error(msgRslv.resolve(err.get()));
return;
}
SDf55 sDf55 = validator.getStored(ValidationStored.Sdf55ByGroupId);
SDf54 sDf54 = validator.getStored(ValidationStored.Sdf55Sdf54ByDocnmRef);
PaymentInstruction pmt = validator.getStored(ValidationStored.Sdf55PaymentInstruction);
TradingClearingRegistry tcr = validator.getStored(ValidationStored.Sdf55Tcr);
AssetTrio assetTrio = validator.getStored(ValidationStored.Sdf55Assets);
log.debug("sdf55.id={}, found sdf54.id={}, PaymentInstruction.id={}", sDf55.getId(), sDf54.getId(), pmt.getId());
pmt.setTransactionStatus(TransactionStatus.ok.getKey());
pmt.setUpdated(Instant.now());
pmtImdg.update(pmt);
if (tcr != null) {
dmiService.setOk(tcr.getId(), CurrencyCode.RUB.getKey(), sDf54.getDocnm_ref());
assets.process(assetTrio.a__b(),
assetTrio.a__t(),
assetTrio.a__f(),
BigDecimal.ZERO);
}
}
}

View file

@ -25,6 +25,7 @@ import ru.spcex.clearing.service.AssetTrio;
import ru.spcex.clearing.service.Sdf54Creator;
import ru.spcex.clearing.service.builder.PaymentInstructionBuilderV2;
import ru.spcex.clearing.service.registry.AssetTBFProcessing;
import ru.spcex.clearing.service.registry.DmiService;
import ru.spcex.clearing.service.registry.RegistryManager;
import ru.spcex.clearing.service.validation.ValidationStored;
import ru.spcex.clearing.util.security.UserRoleVerification;
@ -37,7 +38,6 @@ import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.enumeration.IEnumId;
import ru.spcex.platform.utils.enumeration.IMessageResolver;
import ru.spcex.platform.utils.number.BigDecimalUtil;
import ru.spcex.platform.utils.validation.IValidator;
import java.math.BigDecimal;
@ -45,6 +45,8 @@ import java.time.Instant;
import java.util.Optional;
import java.util.function.Function;
import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD;
@Service
public class PaymentInstructionOutboundService {
private final Logger log = LoggerFactory.getLogger(getClass());
@ -64,12 +66,13 @@ public class PaymentInstructionOutboundService {
private final Sdf54Creator sdf54Creator;
private final KafkaSender kafkaSender;
private final AssetTBFProcessing assetsMng;
private final DmiService dmiService;
@Autowired
public PaymentInstructionOutboundService(UserRoleVerification rights,
IMessageResolver msgs,
ImdgProvider imdgProvider,
Function<PIClearingOutbondActionNewRequest, IValidator> validation, RegistryManager rgsMng, KafkaSender kafkaSender, AssetTBFProcessing assetsMng) {
Function<PIClearingOutbondActionNewRequest, IValidator> validation, RegistryManager rgsMng, KafkaSender kafkaSender, AssetTBFProcessing assetsMng, DmiService dmiService) {
this.imdgProvider = imdgProvider;
this.rights = rights;
this.msgs = msgs;
@ -86,6 +89,7 @@ public class PaymentInstructionOutboundService {
this.rgsMng = rgsMng;
this.kafkaSender = kafkaSender;
this.assetsMng = assetsMng;
this.dmiService = dmiService;
}
public RequestInfoUpdate sendOutBoundPayment(BaseRequest<PIClearingOutbondActionNewRequest> req) {
@ -104,7 +108,7 @@ public class PaymentInstructionOutboundService {
Account accDeb = validator.getStored(ValidationStored.PIOutboundAccountDeb);
Company addressee = cmpImdg.getSingleObjectByID(payload.getAddresseeId());
Company sender = cmpImdg.getSingleObjectByID(payload.getSenderId());
BigDecimal amount = BigDecimalUtil.safeBD(payload.getCreditLeg_amount());
BigDecimal amount = safeBD(payload.getCreditLeg_amount());
log.debug("all checks passed, accCred.id={}, accDeb.id={}, addressee.id={}, sender.id={}, amount: {}",
accCred.getId(), accDeb.getId(), addressee.getId(), sender.getId(), amount);
@ -153,12 +157,18 @@ public class PaymentInstructionOutboundService {
}
log.debug("assets found and balance is ok. am*b.id={}, am*t.id={}, am*f.id={}",
assets.get().a__b().getId(), assets.get().a__t().getId(), assets.get().a__f().getId());
assetsMng.process(assets.get().a__b(), assets.get().a__t(), assets.get().a__f(), amount.negate());
SDf54 sDf54 = sdf54Creator.create(pmt);
sDf54.setId(idGenerator.nextId());
sDf54.setGenerationId(pmt.getId());
sdf54Imdg.insert(sDf54);
log.debug("new sdf54.id: {}", sDf54.getId());
if (tcr != null) {
dmiService.setProc(tcr.getId(),
CurrencyCode.RUB.getKey(),
safeBD(amount).negate(),
sDf54.getDocnm_ref());
assetsMng.process(assets.get().a__b(), assets.get().a__t(), assets.get().a__f(), BigDecimal.ZERO);
}
SdfClearingRequest exp = new SdfClearingRequest();
exp.setGroupId(sDf54.getGenerationId());
kafkaSender.sendRequestToQueue(Consts.SDF54_PROCESS, exp);

View file

@ -2,6 +2,7 @@ package ru.spcex.clearing.service.registry;
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;
@ -33,11 +34,17 @@ public class AssetTBFProcessing {
private final Imdg<Registry> rgsImdg;
private final RegistryManager rgsMng;
@Autowired
public AssetTBFProcessing(ImdgProvider imdgProvider, RegistryManager rgsMng) {
this.rgsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.rgsMng = rgsMng;
}
public AssetTBFProcessing(Imdg<Registry> rgsImdg) {
this.rgsImdg = rgsImdg;
this.rgsMng = new RegistryManager(rgsImdg);
}
public void process(Registry am_b, Registry am_t, Registry am_f, BigDecimal sum) {
safeWrapField(am_t::getBalance, am_t::setBalance);
safeWrapField(am_b::getBalance, am_b::setBalance);

View file

@ -34,7 +34,7 @@ public class DmiService {
public void setProc(Long tcrId,
String securitySymbol,
BigDecimal balance,
BigDecimal contract) {
String contract) {
findDmi(tcrId, securitySymbol, contract.toString()).ifPresentOrElse(d__i -> {
d__i.setRegistryStatus(RegistryStatus.PROC.getKey());
d__i.setUpdated(Instant.now());

View file

@ -0,0 +1,113 @@
package ru.spcex.clearing.service.validation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.sdf.SDf54;
import ru.clearing.classes.statics.data.sdf.SDf55;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
import ru.spcex.clearing.service.AssetTrio;
import ru.spcex.clearing.service.registry.AssetTBFProcessing;
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.Optional;
public enum Sdf55ValidationRule implements IValidationRule<ImdgValidationContext<StatementRequest>> {
Fields() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
StatementRequest validatedObject = context.getValidatedObject();
if (validatedObject.getGroupId() == null) {
return of(ClearingError.WrongField, "groupId");
}
return empty();
}
},
GenerationIdFound() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
StatementRequest validatedObject = context.getValidatedObject();
Long groupId = validatedObject.getGroupId();
Imdg<SDf55> sDf55Imdg = context.obtainMap(IMDGDistributedNames.Map_SDf55, SDf55.class);
SDf55 sDf55 = sDf55Imdg.getFirstObjectBySQL("generationId = %d".formatted(groupId));
if (sDf55 == null) {
return of(ClearingError.RecordNotFound, "SDf55.id=%d".formatted(groupId));
}
context.storeObject(ValidationStored.Sdf55ByGroupId, sDf55);
return empty();
}
},
Sdf54Present() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
SDf55 sDf55 = context.getStoredObject(ValidationStored.Sdf55ByGroupId);
Imdg<SDf54> sDf54Imdg = context.obtainMap(IMDGDistributedNames.Map_SDf54, SDf54.class);
SDf54 sDf54 = sDf54Imdg.getFirstObjectBySQL("docnm_ref = '%s'".formatted(sDf55.getDocnm_ref()));
if (sDf54 == null) {
return of(ClearingError.RecordNotFound, "SDf54.docnm_ref=%s".formatted(sDf55.getDocnm_ref()));
}
context.storeObject(ValidationStored.Sdf55Sdf54ByDocnmRef, sDf54);
return empty();
}
},
PaymentInstructionPresent() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
SDf54 sDf54 = context.getStoredObject(ValidationStored.Sdf55Sdf54ByDocnmRef);
Imdg<PaymentInstruction> pmtImdg = context.obtainMap(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class);
PaymentInstruction paymentInstruction = pmtImdg.getSingleObjectByID(sDf54.getGenerationId());
if (paymentInstruction == null) {
return of(ClearingError.RecordNotFound, "PaymentInstruction.id=%d".formatted(sDf54.getGenerationId()));
}
context.storeObject(ValidationStored.Sdf55PaymentInstruction, paymentInstruction);
return empty();
}
},
TcrPresent() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
Imdg<TradingClearingRegistry> tcrImdg = context.obtainMap(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class);
PaymentInstruction pmt = context.getStoredObject(ValidationStored.Sdf55PaymentInstruction);
Long accCredId = pmt.getCreditLeg_accountId();
Long addresseeId = pmt.getSenderId();
if (accCredId == null || addresseeId == null) return empty();
TradingClearingRegistry tcr = tcrImdg.getFirstObjectBySQL(
"moneyAccountId = '%d' and companyId = %d".formatted(accCredId, addresseeId)
);
if (tcr != null) {
context.storeObject(ValidationStored.Sdf55Tcr, tcr);
}
return empty();
}
},
AssetsPresent() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<StatementRequest> context) {
Imdg<Registry> rgsImdg = context.obtainMap(IMDGDistributedNames.Map_Registry, Registry.class);
PaymentInstruction pmt = context.getStoredObject(ValidationStored.Sdf55PaymentInstruction);
Long accCredId = pmt.getCreditLeg_accountId();
Long addresseeId = pmt.getSenderId();
AssetTBFProcessing assets = new AssetTBFProcessing(rgsImdg);
Optional<AssetTrio> assetTrio = assets.searchMoneyByAccAndCompany(addresseeId, accCredId);
if (assetTrio.isEmpty()) {
return of(ClearingError.RecordNotFound, "AM*F/AM*B/AM*T");
}
context.storeObject(ValidationStored.Sdf55Assets, assetTrio.get());
return empty();
}
}
;
private final static Logger log = LoggerFactory.getLogger(Sdf55ValidationRule.class);
@Override
public String ruleName() {
return "Sdf55ValidationRule." + name();
}
}

View file

@ -26,6 +26,8 @@ public enum ValidationStored {
Sdf20Account, Sdf20Company, Sdf20Tcr, Sdf20Dst, Sdf20Ast, Sdf20Security,
Sdf55ByGroupId, Sdf55Sdf54ByDocnmRef, Sdf55PaymentInstruction, Sdf55Tcr, Sdf55Assets,
SecurityBySecurityCode,
RegistrysByContract

View file

@ -145,9 +145,16 @@ public class InspectionObligations implements ISessionStage {
}
}
for (Registry rgs : group) {
Runnable failGroup = () -> Stream.concat(group.stream(), getRefundDateRgsIfPresent(group).stream())
.forEach(registry -> {
registry.setComment(msgResolver.resolve(RgsError.A__tNotFound));
updateRegistryStatus(registry, RegistryStatus.FAIL);
});
RegistryInstrumentType mOrS = IEnumKey.getEnumByKey(RegistryInstrumentType.class, rgs.getRegistryInstrumentType());
if (mOrS == null) {
log.error("RegistryInstrumentType is null for {}.id={}", rgs.getRegistryCode(), rgs.getId());
log.error("RegistryInstrumentType is null for groupId {} {}.id={}", entry.getKey(),
rgs.getRegistryCode(), rgs.getId());
failGroup.run();
continue;
}
if (Section.MKR.equals(section) && mOrS.equals(RegistryInstrumentType.S)) {
@ -157,11 +164,7 @@ public class InspectionObligations implements ISessionStage {
if (a__t.isEmpty()) {
log.debug("groupId {}, {}.id={} - A**T not found. settings FAIL to group",
entry.getKey(), rgs.getRegistryCode(), rgs.getId());
Stream.concat(group.stream(), getRefundDateRgsIfPresent(group).stream())
.forEach(r -> {
r.setComment(msgResolver.resolve(RgsError.A__tNotFound));
updateRegistryStatus(r, RegistryStatus.FAIL);
});
failGroup.run();
continue GROUP;
}
}