diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/PaymentInstructionGroup.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/PaymentInstructionGroup.java new file mode 100644 index 000000000..dfe4b217c --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/PaymentInstructionGroup.java @@ -0,0 +1,107 @@ +package ru.spcex.clearing.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.clearing.classes.statics.data.account.Account; +import ru.clearing.classes.statics.data.payment.PaymentInstruction; +import ru.spcex.clearing.service.group.Direction; +import ru.spcex.clearing.service.payment.group.PaymentGroup; +import ru.spcex.platform.enumeration.Sender; + +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Function; + +public class PaymentInstructionGroup { + private Logger log = LoggerFactory.getLogger(getClass()); + private Account tranAcc; + + public static PaymentInstructionGroup builder() { + return new PaymentInstructionGroup(); + } + + public PaymentInstructionGroup tran(Account acc) { + this.tranAcc = acc; + return this; + } + + private PaymentInstructionGroup() {} + + public Collection group(List pmts) { + Collection result = new ArrayList<>(); + pmts = pmts + .stream() + .sorted(Comparator.comparing(PaymentInstruction::getId)) + .toList(); + for (int i = 0; i < pmts.size(); i++) { + PaymentInstruction pmt = pmts.get(i); + if (pmt == null) { + continue; + } + Direction direction = getDirection(pmt); + if (direction.equals(Direction.unknown)) { + result.add(new PaymentGroup(pmt)); + } + LinkedList relatedPmts = new LinkedList<>(); + relatedPmts.add(pmt); + for (int j = i + 1; j < pmts.size(); j++) { + PaymentInstruction related = pmts.get(j); + if (related == null) { + continue; + } + if (pmtsOfTheSameAgentsToSpvb(pmt, related) || pmtsOfTheSameAgentsFromSpvb(pmt, related)) { + relatedPmts.add(related); + pmts.set(j, null); + } + } + PaymentGroup paymentGroup = new PaymentGroup(); + paymentGroup.setPaymentInstructions(relatedPmts); + paymentGroup.setDirection(direction); + Function amountGetter; + switch (direction) { + case to_spvb -> amountGetter = PaymentInstruction::getCreditLeg_amount; + case from_spvb -> amountGetter = PaymentInstruction::getDebitLeg_amount; + default -> throw new IllegalStateException("Unexpected value: " + direction); + } + paymentGroup.setSum(relatedPmts + .stream() + .map(amountGetter) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + result.add(paymentGroup); + } + return result; + } + + private boolean pmtsOfTheSameAgentsToSpvb(PaymentInstruction pmt, PaymentInstruction related) { + return addresseeIsSpvb(pmt) + && addresseeIsSpvb(related) + && Objects.equals(pmt.getCreditLeg_accountId(), related.getCreditLeg_accountId()) + && Objects.equals(pmt.getPaymentPurpose(), related.getPaymentPurpose()); + } + + private boolean pmtsOfTheSameAgentsFromSpvb(PaymentInstruction pmt, PaymentInstruction related) { + return senderIsSpvb(pmt) + && senderIsSpvb(related) + && Objects.equals(pmt.getDebitLeg_accountId(), related.getDebitLeg_accountId()) + && Objects.equals(pmt.getPaymentPurpose(), related.getPaymentPurpose()); + } + + private Direction getDirection(PaymentInstruction p) { + if (addresseeIsSpvb(p)) { + return Direction.to_spvb; + } else if (senderIsSpvb(p)) { + return Direction.from_spvb; + } else { + return Direction.unknown; + } + } + + private boolean addresseeIsSpvb(PaymentInstruction p) { + return p.getAddresseeId().equals(Sender.One.getId()) && p.getDebitLeg_accountId().equals(tranAcc.getId()); + } + + private boolean senderIsSpvb(PaymentInstruction p) { + return p.getSenderId().equals(Sender.One.getId()) && p.getCreditLeg_accountId().equals(tranAcc.getId()); + } + +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Sdf03Creator.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Sdf03Creator.java index 93d0d2f9a..b3074ff54 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Sdf03Creator.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/Sdf03Creator.java @@ -9,6 +9,7 @@ import ru.clearing.classes.statics.data.payment.PaymentInstruction; import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; import ru.clearing.classes.statics.data.sdf.SDf03; import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.service.payment.group.PaymentGroup; import ru.spcex.platform.enumeration.AccountType; import ru.spcex.platform.enumeration.Status; import ru.spcex.platform.imdg.api.Imdg; @@ -20,6 +21,7 @@ import ru.spcex.platform.utils.time.TimeUtil; import java.time.Instant; import java.time.format.DateTimeFormatter; +import java.util.Optional; @Component public class Sdf03Creator { @@ -114,6 +116,17 @@ public class Sdf03Creator { return sDf03; } + public SDf03 create(PaymentGroup group) { + Optional pmt = group.getPaymentInstructions().stream().findFirst(); + if (pmt.isEmpty()) { + throw new IllegalStateException("empty group"); //never + } + SDf03 sDf03 = create(pmt.get()); + String sumDeb = group.getSum() != null ? group.getSum().toString() : ""; + sDf03.setSum_deb(BigDecimalUtil.limitDecimalPlaces(sumDeb, 2)); + return sDf03; + } + private AccountType getAccountType(Long accountId) { Account account = accountImdg.getSingleObjectByID(accountId); return IEnumKey.getEnumByKey(AccountType.class, account.getAccountType()); diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/payment/group/PaymentGroup.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/payment/group/PaymentGroup.java new file mode 100644 index 000000000..f5985de7d --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/payment/group/PaymentGroup.java @@ -0,0 +1,48 @@ +package ru.spcex.clearing.service.payment.group; + +import ru.clearing.classes.statics.data.payment.PaymentInstruction; +import ru.spcex.clearing.service.group.Direction; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.List; + +public class PaymentGroup { + private List paymentInstructions; + private BigDecimal sum; + private Direction direction; + + public PaymentGroup() { + } + + public PaymentGroup(PaymentInstruction pmt) { + this.paymentInstructions = Collections.singletonList(pmt); + //fixme в старом заполнении нашел только поле sDf03.setSum_deb (pmt.getDebitLeg_amount) + this.sum = pmt.getDebitLeg_amount(); + this.direction = Direction.unknown; + } + + public List getPaymentInstructions() { + return paymentInstructions; + } + + public void setPaymentInstructions(List paymentInstructions) { + this.paymentInstructions = paymentInstructions; + } + + public BigDecimal getSum() { + return sum; + } + + public void setSum(BigDecimal sum) { + this.sum = sum; + } + + public Direction getDirection() { + return direction; + } + + public void setDirection(Direction direction) { + this.direction = direction; + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDealsFinalMkr.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDealsFinalMkr.java index ed4f08125..cc3facc16 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDealsFinalMkr.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDealsFinalMkr.java @@ -19,9 +19,11 @@ import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.balance.ExportToFileRequest; import ru.spcex.clearing.platform.messaging.domain.cud.importexport.SwtExporterRequest; import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.service.PaymentInstructionGroup; import ru.spcex.clearing.service.RegistryManager; import ru.spcex.clearing.service.Sdf03Creator; import ru.spcex.clearing.service.builder.PaymentInstructionBuilderFinalMkrDeals; +import ru.spcex.clearing.service.payment.group.PaymentGroup; import ru.spcex.clearing.session.stage.ISessionStage; import ru.spcex.clearing.session.stage.StageResult; import ru.spcex.clearing.session.stage.Task; @@ -194,7 +196,7 @@ public class FormingPaymentInstructionDealsFinalMkr implements ISessionStage { cm_t.getId(), lm_t.getId()); String paymentPurpose = section != null && section == Section.FOND ? "" : - "Размещение депозита " + lm_t.getContract() + " по ТКР " + lm_t.getTradingClearingRegistry(); + "Перевод по итогу клиринга по ТКР " + lm_t.getTradingClearingRegistry(); Pair pmtInstrs = PaymentInstructionBuilderFinalMkrDeals .builder(imdgProvider) .cm_t(cm_t) @@ -218,7 +220,7 @@ public class FormingPaymentInstructionDealsFinalMkr implements ISessionStage { paymentInstructionDeals.size()); List returnsAndDeals = Stream.concat(paymentInstructionReturns.stream(), paymentInstructionDeals.stream()).toList(); if (sendSdfs) { - sendSdfs(returnsAndDeals); + sendSdfs(returnsAndDeals, tranAcc); } StageResult> stageResult = new StageResult<>(null, true); stageResult.setStageResult(returnsAndDeals); @@ -231,26 +233,39 @@ public class FormingPaymentInstructionDealsFinalMkr implements ISessionStage { registryImdg.update(rgs); } - private void sendSdfs(List formedPaymentInstructions) { - List sDf03Created = new ArrayList<>(); + private void sendSdfs(List formedPaymentInstructions, Account tranAcc) { List sDf12Created = new ArrayList<>(); + List forSdf03 = new ArrayList<>(); for (PaymentInstruction paymentInstruction : formedPaymentInstructions) { Account account = accountImdg.getSingleObjectByID(paymentInstruction.getCreditLeg_accountId()); Security security = securityImdg.getSingleObjectByID(paymentInstruction.getCreditLeg_securityId()); if (List.of(AccountType.Corr, AccountType.Clrn, AccountType.Tran, AccountType.Anlt, AccountType.Info) .contains(IEnumKey.getEnumByKey(AccountType.class, account.getAccountType()))) { //если info подставить anlt (единственный счет в системе) - sDf03Created.add(sdf03Creator.create(paymentInstruction)); + forSdf03.add(paymentInstruction); } else if (!InstrumentType.CRNC.equalsByKey(security.getInstrumentType()) && List.of(AccountType.Depo, AccountType.Dtrn).contains(IEnumKey.getEnumByKey(AccountType.class, account.getAccountType()))) { sDf12Created.add(newSDf12(paymentInstruction)); } } - - Long sdf03GroupId = !sDf03Created.isEmpty() ? imdgProvider.getImdgIdGenerator().nextId() : null; - for (SDf03 sDf03 : sDf03Created) { + Long sdf03GroupId = !forSdf03.isEmpty() ? imdgProvider.getImdgIdGenerator().nextId() : null; + Collection groups = PaymentInstructionGroup.builder() + .tran(tranAcc) + .group(forSdf03); + for(PaymentGroup group : groups) { + SDf03 sDf03 = sdf03Creator.create(group); sDf03.setGenerationId(sdf03GroupId); sDf03Imdg.insert(sDf03); + log.debug("created SDF03.groupId/id={}/{} for {} by paymentInstructions: {} ", + sdf03GroupId, + sDf03.getId(), + group.getPaymentInstructions().size(), + group.getPaymentInstructions() + .stream() + .map(PaymentInstruction::getId) + .map(String::valueOf) + .collect(Collectors.joining(",", "[", "]")) + ); } if (sdf03GroupId != null) { diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDepositReturn.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDepositReturn.java index 6811a846d..c5219369e 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDepositReturn.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionDepositReturn.java @@ -189,7 +189,7 @@ public class FormingPaymentInstructionDepositReturn implements ISessionStage { .cm_t(cm_t) .tranAcc(tranAcc) .sessionId(sessionId) - .paymentPurpose("Возврат депозита " + cm_t.getContract() + " по ТКР " + cm_t.getTradingClearingRegistry()) + .paymentPurpose("Перевод по итогу клиринга по ТКР " + cm_t.getTradingClearingRegistry()) .build(); paymentInstructionImdg.insert(pmts.getFirst()); paymentInstructionImdg.insert(pmts.getSecond()); diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionReturnMkr.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionReturnMkr.java index a8017181f..89f7dceea 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionReturnMkr.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/session/stage/impl/FormingPaymentInstructionReturnMkr.java @@ -195,7 +195,7 @@ public class FormingPaymentInstructionReturnMkr implements ISessionStage { .cm_t(cm_t) .tranAcc(tranAcc) .sessionId(sessionId) - .paymentPurpose("Возврат депозита " + cm_t.getContract() + " по ТКР " + cm_t.getTradingClearingRegistry()) + .paymentPurpose("Перевод по итогу клиринга по ТКР " + cm_t.getTradingClearingRegistry()) .build(); paymentInstructionImdg.insert(pmts.getFirst()); paymentInstructionImdg.insert(pmts.getSecond()); diff --git a/clearing-parent/dictionary/src/main/java/ru/spcex/clearing/service/group/Direction.java b/clearing-parent/dictionary/src/main/java/ru/spcex/clearing/service/group/Direction.java new file mode 100644 index 000000000..5454f81a1 --- /dev/null +++ b/clearing-parent/dictionary/src/main/java/ru/spcex/clearing/service/group/Direction.java @@ -0,0 +1,5 @@ +package ru.spcex.clearing.service.group; + +public enum Direction { + to_spvb, from_spvb, unknown; +}