diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionAssets.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionAssets.java index e8b43522e..e59bdab46 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionAssets.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionAssets.java @@ -2,12 +2,16 @@ package ru.spcex.clearing.session.stage.impl; import java.math.BigDecimal; import java.time.Instant; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,11 +21,13 @@ import org.springframework.context.annotation.Scope; 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.SettlementHouseProperties; import ru.clearing.classes.statics.data.payment.PaymentInstruction; import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.sdf.SDf03; import ru.clearing.classes.statics.data.sdf.SDf12; import ru.clearing.classes.statics.data.security.Security; +import ru.clearing.classes.statics.data.statement.Statement; import ru.spcex.clearing.error.ClearingError; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.Consts; @@ -30,23 +36,28 @@ import ru.spcex.clearing.platform.messaging.domain.cud.importexport.SwtExporterR import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; import ru.spcex.clearing.service.Sdf03Creator; import ru.spcex.clearing.service.builder.PaymentInstructionBuilderV2; +import ru.spcex.clearing.service.registry.AssetTBFProcessing; import ru.spcex.clearing.service.registry.PaymentStateMarkService; import ru.spcex.clearing.service.registry.RegistryManager; 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.FormingPaymentInstructionPayload; +import ru.spcex.clearing.session.stage.util.RegistryUtil; import ru.spcex.platform.enumeration.AccountStatus; import ru.spcex.platform.enumeration.AccountType; import ru.spcex.platform.enumeration.Allowed; import ru.spcex.platform.enumeration.InstrumentType; +import ru.spcex.platform.enumeration.OperationStatus; import ru.spcex.platform.enumeration.RegistryDesignation; import ru.spcex.platform.enumeration.RegistryStatus; import static ru.spcex.platform.enumeration.RegistryTradingParams.*; +import ru.spcex.platform.enumeration.RegistryUnit; import ru.spcex.platform.enumeration.SdfTable; import ru.spcex.platform.enumeration.Section; import ru.spcex.platform.enumeration.Sender; import ru.spcex.platform.enumeration.SessionType; +import ru.spcex.platform.enumeration.StatementType; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgId; import ru.spcex.platform.imdg.api.ImdgProvider; @@ -54,12 +65,14 @@ 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; import ru.spcex.platform.imdg.api.predicate.specific.SecuritySelector; +import ru.spcex.platform.utils.collection.Pair; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.enumeration.IEnumKey; import ru.spcex.platform.utils.enumeration.IMessageResolver; import ru.spcex.platform.utils.enumeration.SimpleMessageResolver; import ru.spcex.platform.utils.number.BigDecimalUtil; import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD; +import ru.spcex.platform.utils.text.TextUtil; @Service @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @@ -69,12 +82,14 @@ public class FormingPaymentInstructionAssets implements ISessionStage { private ImdgProvider imdgProvider; private ImdgId idGenerator; private Imdg registryImdg; + private Imdg stmtImdg; private Imdg paymentInstructionImdg; private Imdg securityImdg; private Imdg accountImdg; private Imdg companyImdg; private Imdg sDf03Imdg; private Imdg sDf12Imdg; + private Imdg stlmHPropsImdg; private final Sdf03Creator sdf03Creator; private KafkaSender kafkaSender; private final IMessageResolver msgResolver = new SimpleMessageResolver(); @@ -83,10 +98,11 @@ public class FormingPaymentInstructionAssets implements ISessionStage { private Section section; private SessionType sessionType; private final PaymentStateMarkService dmvSrv; + private final AssetTBFProcessing assets; @Autowired public FormingPaymentInstructionAssets(ImdgProvider imdgProvider, - Sdf03Creator sdf03Creator, KafkaSender kafkaSender, RegistryManager rgsMng, PaymentStateMarkService dmvSrv) { + Sdf03Creator sdf03Creator, KafkaSender kafkaSender, RegistryManager rgsMng, PaymentStateMarkService dmvSrv, AssetTBFProcessing assets) { this.sdf03Creator = sdf03Creator; this.kafkaSender = kafkaSender; this.imdgProvider = imdgProvider; @@ -98,9 +114,12 @@ public class FormingPaymentInstructionAssets implements ISessionStage { this.sDf03Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf03, SDf03.class); this.sDf12Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf12, SDf12.class); this.paymentInstructionImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class); + this.stlmHPropsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SettlementHouseProperties, SettlementHouseProperties.class); + this.stmtImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Statement, Statement.class); this.rgsMng = rgsMng; this.securitySelector = new SecuritySelector<>(imdgProvider, Security.class); this.dmvSrv = dmvSrv; + this.assets = assets; } @Override @@ -157,7 +176,7 @@ public class FormingPaymentInstructionAssets implements ISessionStage { false); } - List paymentInstructions = new ArrayList<>(); + List> paymentInstructions = new ArrayList<>(); for (Registry registry : AMBregistries) { Account tranAcc = accountImdg.getFirstObjectBySQL(("accountType = '%s' " + "and status = '%s' " + @@ -244,7 +263,7 @@ public class FormingPaymentInstructionAssets implements ISessionStage { registry.setPaymentId(paymentInstruction.getId()); registry.setUpdated(now); registryImdg.update(registry); - paymentInstructions.add(paymentInstruction); + paymentInstructions.add(new Pair<>(paymentInstruction, registry)); } if (section == null || !section.equals(Section.MKR)) { Collection ASBregistries = selectASBRegistries(sessionId); @@ -314,21 +333,22 @@ public class FormingPaymentInstructionAssets implements ISessionStage { registry.setPaymentId(paymentInstruction.getId()); registry.setUpdated(now); registryImdg.update(registry); - paymentInstructions.add(paymentInstruction); + paymentInstructions.add(new Pair<>(paymentInstruction, registry)); } } if (payload.getPaymentInstructionReturns().size() > 0) { log.debug("PaymentInstructions return size {}, PaymentInstructions deals size {}. sending SDF03", payload.getPaymentInstructionReturns().size(), paymentInstructions.size()); - paymentInstructions = Stream.concat(payload.getPaymentInstructionReturns().stream(), + paymentInstructions = Stream.concat(payload.getPaymentInstructionReturns().stream() + .map(p -> new Pair(p, null)), paymentInstructions.stream()).toList(); } Collection sdfTables = sendSdfs(paymentInstructions, sessionId, dtrnAcc); StageResult stageResult = new StageResult<>(null, true); - stageResult.setStageResult(new PaymentInfo(paymentInstructions, sdfTables)); + stageResult.setStageResult(new PaymentInfo(paymentInstructions.stream().map(Pair::getFirst).toList(), sdfTables)); return stageResult; } @@ -373,13 +393,20 @@ public class FormingPaymentInstructionAssets implements ISessionStage { registryImdg.update(rgs); } - private Collection sendSdfs(List formedPaymentInstructions, Long sessionId, Account dtrnAcc) { + private Collection sendSdfs(List> formedPaymentInstructions, Long sessionId, Account dtrnAcc) { Collection sentSdfs = new ArrayList<>(); List sDf03Created = new ArrayList<>(); List sDf12Created = new ArrayList<>(); - for (PaymentInstruction paymentInstruction : formedPaymentInstructions) { + for (Pair pair : formedPaymentInstructions) { + PaymentInstruction paymentInstruction = pair.getFirst(); Account account = accountImdg.getSingleObjectByID(paymentInstruction.getCreditLeg_accountId()); Security security = securityImdg.getSingleObjectByID(paymentInstruction.getCreditLeg_securityId()); + if (SessionType.CURR.equals(sessionType)) { + boolean madeStatements = createStatementsIfNeeded(paymentInstruction, pair.getSecond()); + if (madeStatements) { + continue; + } + } if (List.of(AccountType.Corr, AccountType.Clrn, AccountType.Tran, AccountType.Anlt, AccountType.Info) .contains(IEnumKey.getEnumByKey(AccountType.class, account.getAccountType()))) { //если info подставить anlt (единственный счет в системе) @@ -458,6 +485,118 @@ public class FormingPaymentInstructionAssets implements ISessionStage { return sDf12; } + private boolean createStatementsIfNeeded(PaymentInstruction pmt, Registry am_b) { + ImdgPredicateBuilder pb = stlmHPropsImdg.predicateBuilder(); + String curCode = pmt.getCreditLeg_currencyCode(); + boolean noNeedForStatements = stlmHPropsImdg.getFirstObjectByPredicate( + pb.and( + pb.in("currencyCode", curCode), + pb.equals("companyId", Sender.Prc.getId()) + ) + ) != null; + if (noNeedForStatements) return false; + Supplier stmtBldr = () -> { + Statement stmt = new Statement(); + stmt.setSenderId(Sender.One.getId()); + stmt.setStatementType(StatementType.incr.getKey()); + stmt.setComment("Расчеты по сессии"); + stmt.setSettlementDate(LocalDate.now()); + stmt.setOperationStatus(OperationStatus.Executed.getKey());//todo тут что-то с апдейтом статуса на EXEC когда создались регистры с нулевым диффом? + stmt.setClearingDate(LocalDate.now()); + stmt.setCreated(Instant.now()); + stmt.setComment(pmt.getPaymentPurpose()); + return stmt; + }; + Statement stmtDeb = stmtBldr.get(); + Statement stmtCred = stmtBldr.get(); + + stmtCred.setAddresseeId(pmt.getSenderId()); + stmtDeb.setAddresseeId(pmt.getAddresseeId()); + + stmtDeb.setAccountId(pmt.getDebitLeg_accountId()); + stmtDeb.setAccount(pmt.getDebitLeg_account()); + stmtCred.setAccountId(pmt.getCreditLeg_accountId()); + stmtCred.setAccount(pmt.getCreditLeg_account()); + + + stmtDeb.setSecurityId(pmt.getDebitLeg_securityId()); + stmtCred.setSecurityId(pmt.getCreditLeg_securityId()); + + stmtDeb.setInOutDirection(pmt.getDebitLeg_direction()); + stmtCred.setInOutDirection(pmt.getCreditLeg_direction()); + + stmtDeb.setAmount(pmt.getDebitLeg_amount()); + stmtCred.setAmount(pmt.getCreditLeg_amount()); + + stmtImdg.insert(stmtDeb); + stmtImdg.insert(stmtCred); + + if (am_b != null) { + Function copy = (unit) -> { + Registry res = am_b.clone(); + RegistryManager.zeroState(res); + res.setRegistryUnit(RegistryUnit.B.getKey()); + res.setRegistryCode(RegistryUtil.clearingCode(res)); + return res; + }; + + Registry am_t = assets.searchByTcrCompanyAccount(am_b, AM_T).orElseThrow(() -> { + log.error("{}.id={} PaymentInstruction.id={} no AM*T found", am_b.getRegistryCode(), am_b.getId(), pmt.getId()); + return new RuntimeException("couldn't find AM*T"); + }); + + BigDecimal am_bBalance = safeBD(am_b.getBalance()); + am_b.setBalance(BigDecimal.ZERO); + + { + BigDecimal amtBalance = safeBD(am_t.getBalance()); + am_t.setBalance(amtBalance.subtract(am_bBalance)); + } + + Registry am_f = assets.searchByTcrCompanyAccount(am_b, AM_F) + .orElseGet(() -> copy + .andThen(r -> {r.setBalance(am_t.getBalance()); return r;}) + .apply(RegistryUnit.F) + ); + + assets.process(am_b, am_t, am_f, BigDecimal.ZERO); + registryImdg.update(am_t); + } + + log.debug(("paymentInstruction.id=%d created statements [credit id=%d] and [debit id=%d]. " + + "set am_b.id=%d balance to zero").formatted( + pmt.getId(), stmtCred.getId(), stmtDeb.getId(), am_b != null ? am_b.getId() : null + )); + + return true; + } + + private Registry copyRegB(Registry rgs) { + Registry rgsB = rgs.clone(); + RegistryManager.zeroState(rgsB); + rgsB.setRegistryUnit(RegistryUnit.B.getKey()); + rgsB.setRegistryCode(RegistryUtil.clearingCode(rgsB)); + return rgsB; + } + + private Registry copyRegF(Registry rgs) { + Registry rgsF = rgs.clone(); + RegistryManager.zeroState(rgsF); + rgsF.setRegistryUnit(RegistryUnit.F.getKey()); + rgsF.setRegistryCode(RegistryUtil.clearingCode(rgsF)); + rgsF.setBalance(safeBD(rgs.getBalance())); + return rgsF; + } + + DateTimeFormatter payDateFormatter = DateTimeFormatter.ofPattern("dd.MM.yy"); + + private LocalDate settlementDate(String payDate) { + if (TextUtil.isEmpty(payDate)) { + return null; + } + return LocalDate.parse(payDate, payDateFormatter); + } + public void setSection(Section section) { this.section = section; }