This commit is contained in:
parent
8fb186f564
commit
75c4e7aeae
6 changed files with 194 additions and 9 deletions
|
|
@ -5,13 +5,18 @@ import org.springframework.stereotype.Component;
|
|||
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.generated.ClearingMemberCategory;
|
||||
import ru.spcex.clearing.balance.validation.AccountBalanceValidation;
|
||||
import ru.spcex.clearing.balance.validation.ValidationStored;
|
||||
import ru.spcex.clearing.imdg.IMDGDistributedNames;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountBalanceClearingRequest;
|
||||
import ru.spcex.platform.enumeration.AccountType;
|
||||
import ru.spcex.platform.enumeration.BalanceAccountType;
|
||||
import ru.spcex.platform.enumeration.ClearingCategory;
|
||||
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.IEnumKey;
|
||||
import ru.spcex.platform.utils.validation.IValidator;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -26,12 +31,14 @@ public class AccountBalanceService {
|
|||
private final ImdgProvider imdgProvider;
|
||||
private final Imdg<AccountBalance> accountBalanceImdg;
|
||||
private final Function<AccountBalanceValidation, IValidator> validationFactory;
|
||||
private final Imdg<ClearingMemberCategory> clearingCategoryImdg;
|
||||
|
||||
public AccountBalanceService(ImdgProvider imdgProvider,
|
||||
@Qualifier("accountBalanceValidator") Function<AccountBalanceValidation, IValidator> validationFactory) {
|
||||
this.imdgProvider = imdgProvider;
|
||||
this.validationFactory = validationFactory;
|
||||
this.accountBalanceImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class);
|
||||
this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class);
|
||||
}
|
||||
|
||||
public AccountResult createAccountBalance(Long addresseeId, Long accountId, BigDecimal amount,
|
||||
|
|
@ -85,4 +92,88 @@ public class AccountBalanceService {
|
|||
if (b == null) return a;
|
||||
return a.add(b);
|
||||
}
|
||||
|
||||
public void updateAccountBalanceByClearing(AccountBalanceClearingRequest req) {
|
||||
ClearingCategory category = getClearingCategoryByCompanyId(req.getCompanyId());
|
||||
if (category.equals(ClearingCategory.I)) {
|
||||
updateAccountCategoryIClrn(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount());
|
||||
updateAccountCategoryITran(req.getFirstLegAmount());
|
||||
} else if (category.equals(ClearingCategory.V)) {
|
||||
updateAccountCategoryVInfo(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount());
|
||||
updateAccountCategoryVAnlt(req.getFirstLegAmount());
|
||||
} else if (category.equals(ClearingCategory.B)) {
|
||||
updateAccountCategoryBClrn(req.getAccountId(), req.getCompanyId(), req.getFirstLegAmount());
|
||||
}
|
||||
//todo send message success to Kafka
|
||||
}
|
||||
|
||||
private ClearingCategory getClearingCategoryByCompanyId(Long companyId) {
|
||||
ClearingMemberCategory category = clearingCategoryImdg.getSingleObjectByFieldValues(
|
||||
Map.of("companyId", companyId));
|
||||
return IEnumKey.getEnumByKey(ClearingCategory.class,
|
||||
category.getClearingMemberCategory());
|
||||
}
|
||||
|
||||
private void updateAccountCategoryIClrn(Long accountId, Long companyId, BigDecimal firstLegAmount) {
|
||||
AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Clrn);
|
||||
BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount();
|
||||
BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount);
|
||||
accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setUpdated(Instant.now());
|
||||
accountBalanceImdg.update(accountBalance);
|
||||
}
|
||||
|
||||
private void updateAccountCategoryITran(BigDecimal firstLegAmount) {
|
||||
AccountBalance accountBalance = loadAccountBalance(AccountType.Tran);
|
||||
BigDecimal freeBalanceAmount = accountBalance.getFreeBalanceAmount();
|
||||
accountBalance.setFreeBalanceAmount(freeBalanceAmount.add(firstLegAmount.abs()));
|
||||
accountBalance.setUpdated(Instant.now());
|
||||
accountBalanceImdg.update(accountBalance);
|
||||
}
|
||||
|
||||
private void updateAccountCategoryVInfo(Long accountId, Long companyId, BigDecimal firstLegAmount) {
|
||||
AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Info);
|
||||
BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount();
|
||||
BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount);
|
||||
accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setUpdated(Instant.now());
|
||||
accountBalanceImdg.update(accountBalance);
|
||||
}
|
||||
|
||||
private void updateAccountCategoryVAnlt(BigDecimal firstLegAmount) {
|
||||
AccountBalance accountBalance = loadAccountBalance(AccountType.Anlt);
|
||||
BigDecimal previousFreeBalanceAmount = accountBalance.getFreeBalanceAmount();
|
||||
BigDecimal resultFreeBalanceAmount = previousFreeBalanceAmount.subtract(firstLegAmount);
|
||||
accountBalance.setFreeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setChangeBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setBalanceAmount(resultFreeBalanceAmount);
|
||||
accountBalance.setUpdated(Instant.now());
|
||||
accountBalanceImdg.update(accountBalance);
|
||||
}
|
||||
|
||||
private void updateAccountCategoryBClrn(Long accountId, Long companyId, BigDecimal firstLegAmount) {
|
||||
AccountBalance accountBalance = loadAccountBalance(accountId, companyId, AccountType.Clrn);
|
||||
BigDecimal freeBalanceAmount = accountBalance.getFreeBalanceAmount();
|
||||
accountBalance.setDebitAmount(plus(freeBalanceAmount, firstLegAmount.abs()));
|
||||
accountBalance.setUpdated(Instant.now());
|
||||
accountBalanceImdg.update(accountBalance);
|
||||
}
|
||||
|
||||
private AccountBalance loadAccountBalance(Long accountId, Long companyId, AccountType type) {
|
||||
return accountBalanceImdg.getSingleObjectByFieldValues(
|
||||
Map.of("accountId", accountId, "companyId", companyId,
|
||||
"accountType", type.getKey())
|
||||
);
|
||||
}
|
||||
|
||||
private AccountBalance loadAccountBalance(AccountType type) {
|
||||
return accountBalanceImdg.getSingleObjectByFieldValues(
|
||||
Map.of("companyId", 1L,
|
||||
"accountType", type.getKey())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
|
|||
import ru.spcex.clearing.platform.messaging.domain.Consts;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdf01Request;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdfRequestPart;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountBalanceClearingRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.balance.ExportToFileRequest;
|
||||
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
|
||||
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
|
||||
|
|
@ -38,14 +39,16 @@ public class StatementService extends QueueConsumer implements InitializingBean
|
|||
private final KafkaSender kafkaReqProducer;
|
||||
private final Map<SdfTable, Imdg<? extends WithAccount>> sdfImdgs;
|
||||
private final Map<SdfTable, AbstractExecutor<? extends WithAccount>> executorsMap;
|
||||
private final AccountBalanceService accountBalanceService;
|
||||
|
||||
@Autowired
|
||||
public StatementService(Consumer<String, Object> kafkaQueue,
|
||||
ImdgProvider imdgProvider,
|
||||
KafkaSender kafkaReqProducer,
|
||||
@Qualifier("sdfExecutors") Map<SdfTable, AbstractExecutor<? extends WithAccount>> executorsMap) {
|
||||
@Qualifier("sdfExecutors") Map<SdfTable, AbstractExecutor<? extends WithAccount>> executorsMap, AccountBalanceService accountBalanceService) {
|
||||
super(kafkaQueue);
|
||||
this.imdgProvider = imdgProvider;
|
||||
this.accountBalanceService = accountBalanceService;
|
||||
this.sdfImdgs = new EnumMap<>(SdfTable.class);
|
||||
this.kafkaReqProducer = kafkaReqProducer;
|
||||
this.executorsMap = executorsMap;
|
||||
|
|
@ -59,9 +62,16 @@ public class StatementService extends QueueConsumer implements InitializingBean
|
|||
callback(StatementRequest.class)
|
||||
.setConsumer(this::process)
|
||||
.forDestination(Consts.STATEMENT_PROCESS, callbacks::put);
|
||||
callback(AccountBalanceClearingRequest.class)
|
||||
.setConsumer(this::accountBalanceClearingUpdate)
|
||||
.forDestination(Consts.BALANCE_ACCOUNT_UPDATE, callbacks::put);
|
||||
init();
|
||||
}
|
||||
|
||||
private void accountBalanceClearingUpdate(BaseRequest<AccountBalanceClearingRequest> updateAccBalanceReq) {
|
||||
accountBalanceService.updateAccountBalanceByClearing(updateAccBalanceReq.getRequestPayload());
|
||||
}
|
||||
|
||||
private void process(BaseRequest<StatementRequest> systemRequest) {
|
||||
StatementRequest statementRequest = systemRequest.getRequestPayload();
|
||||
Collection<? extends WithAccount> sdfGroup;
|
||||
|
|
|
|||
|
|
@ -12,15 +12,13 @@ import ru.spcex.clearing.service.order.ExecutionDepositSorter;
|
|||
import ru.spcex.platform.enumeration.Allowed;
|
||||
import ru.spcex.platform.enumeration.ClearingCategory;
|
||||
import ru.spcex.platform.imdg.api.Imdg;
|
||||
import ru.spcex.platform.imdg.api.ImdgId;
|
||||
import ru.spcex.platform.imdg.api.ImdgProvider;
|
||||
import ru.spcex.platform.utils.enumeration.EnumMessage;
|
||||
import ru.spcex.platform.utils.validation.IValidator;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Service
|
||||
|
|
@ -30,6 +28,13 @@ public class Clearing {
|
|||
private final Imdg<ClearingMemberCategory> clearingCategoryImdg;
|
||||
private final ExecutionDepositSorter sorter;
|
||||
private final BiFunction<ClearingCategory, ExecutionDeposit, IValidator> validation;
|
||||
private final ImdgId idProvider;
|
||||
//при прохождении по выгруженным ExecutionDeposit, ошибочные статусы проставляются для
|
||||
//контр сделок. В таком случае, в коллекции хранятся не синхронизированные с IMDG ExecutionDeposit
|
||||
//для которых статус должен быть DENIED
|
||||
//************ !!! NOT THREAD SAFE !!! ************
|
||||
private final Set<Long> deniedIds = new HashSet<>();
|
||||
private Long clearingSessionId;
|
||||
|
||||
@Autowired
|
||||
public Clearing(ImdgProvider imdgProvider,
|
||||
|
|
@ -37,11 +42,13 @@ public class Clearing {
|
|||
BiFunction<ClearingCategory, ExecutionDeposit, IValidator> validation) {
|
||||
this.executionDepositImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ExecutionDeposit, ExecutionDeposit.class);
|
||||
this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class);
|
||||
this.idProvider = imdgProvider.getImdgIdGenerator();
|
||||
this.sorter = sorter;
|
||||
this.validation = validation;
|
||||
}
|
||||
|
||||
public void startClearing() {
|
||||
clearingSessionId = idProvider.nextId();
|
||||
//выгружаем ExecutionDeposit с пустым sessionId
|
||||
Collection<ExecutionDeposit> execDeposits = executionDepositImdg.getCollectionObjectsBySQL("sessionId = null");
|
||||
//делаем группировку ExecutionDeposit по категории и компании, делаем сортировку
|
||||
|
|
@ -49,15 +56,29 @@ public class Clearing {
|
|||
|
||||
executionDeposits.forEach((category, companies) -> {
|
||||
for (List<ExecutionDeposit> companyExecDeposits : companies) {
|
||||
processSingleCompany(category, companyExecDeposits);
|
||||
runChecksSetStatus(category, companyExecDeposits);
|
||||
}
|
||||
});
|
||||
executionDeposits.forEach((category, companies) -> {
|
||||
for (List<ExecutionDeposit> companyExecDeposits : companies) {
|
||||
processAllowedExecutionDeposit(category, companyExecDeposits);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void processSingleCompany(ClearingCategory category, List<ExecutionDeposit> executionDeposits) {
|
||||
log.info("Start clearing for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId());
|
||||
//кладем в порядке I инициатор, V внутренний, B банк (ответная категория)
|
||||
//получается I -> B; V -> B
|
||||
//
|
||||
//1) ТОЛЬКО ДЛЯ V: если DENIED по любым причинам то для всех следующих сделок этой компании DENIED
|
||||
//2) DENIED сделка всегда встречная сделка тоже делается DENIED
|
||||
//3) DENIED может быть для сделки которая уже прошла обработку и стала ALLOWED
|
||||
private void runChecksSetStatus(ClearingCategory category, List<ExecutionDeposit> executionDeposits) {
|
||||
log.info("setting statuses for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId());
|
||||
boolean financialError = false;
|
||||
for (ExecutionDeposit execDeposit : executionDeposits) {
|
||||
if (deniedIds.contains(execDeposit.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (financialError) {
|
||||
updateDenied(execDeposit);
|
||||
continue;
|
||||
|
|
@ -80,6 +101,17 @@ public class Clearing {
|
|||
execDeposit.setCoverageStatus(Allowed.DENIED.getKey());
|
||||
execDeposit.setUpdated(Instant.now());
|
||||
executionDepositImdg.update(execDeposit);
|
||||
//также необходимо установить статус DEND встречной сделке контрагента этой компании, которая выбирается из executionDeposit по ключу:
|
||||
//securityId И чтобы сделка была компании категории clearingMemberCategory.clearingMemberCategory ≠ той категории, сделка которой обрабатывается в настоящий момент.
|
||||
ExecutionDeposit matchedExecDeposit = executionDepositImdg.getSingleObjectBySQL(
|
||||
"securityId = " + execDeposit.getSecurityId()
|
||||
+ " AND companyId != " + execDeposit.getCompanyId());
|
||||
if (matchedExecDeposit != null) {
|
||||
matchedExecDeposit.setCoverageStatus(Allowed.DENIED.getKey());
|
||||
matchedExecDeposit.setUpdated(Instant.now());
|
||||
executionDepositImdg.update(matchedExecDeposit);
|
||||
deniedIds.add(matchedExecDeposit.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAllowed(ExecutionDeposit execDeposit) {
|
||||
|
|
@ -87,4 +119,17 @@ public class Clearing {
|
|||
execDeposit.setUpdated(Instant.now());
|
||||
executionDepositImdg.update(execDeposit);
|
||||
}
|
||||
|
||||
private void processAllowedExecutionDeposit(ClearingCategory category, List<ExecutionDeposit> executionDeposits) {
|
||||
log.info("running clearing for category: {}, company: {}", category, executionDeposits.get(0).getCompanyId());
|
||||
for (ExecutionDeposit execDeposit : executionDeposits) {
|
||||
if (deniedIds.contains(execDeposit.getId())) {
|
||||
continue;
|
||||
}
|
||||
if (!Allowed.ALLOWED.getKey().equals(execDeposit.getCoverageStatus())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package ru.spcex.platform.enumeration;
|
|||
import ru.spcex.platform.utils.enumeration.IEnumKey;
|
||||
|
||||
public enum AccountType implements IEnumKey {
|
||||
Clrn("CLRN"), Bank("BANK"), Info("INFO");
|
||||
Clrn("CLRN"), Bank("BANK"), Info("INFO"), Tran("TRAN"), Anlt("ANLT");
|
||||
|
||||
private final String key;
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public interface Consts {
|
|||
String EXPORT_PROCESS = "export-process";
|
||||
String ACCOUNT_NEW = "account-new";
|
||||
String BALANCE_ACCOUNT_NEW = "balance-account-new";
|
||||
String BALANCE_ACCOUNT_UPDATE = "balance-account-update";
|
||||
String LAUNCHER_NEW = "launcher-new";
|
||||
String NOTIFICATION_NEW = "notification-new";
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
package ru.spcex.clearing.platform.messaging.domain.cud.balance;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class AccountBalanceClearingRequest {
|
||||
@JsonProperty
|
||||
private Long accountId;
|
||||
@JsonProperty
|
||||
private Long companyId;
|
||||
@JsonProperty
|
||||
private BigDecimal firstLegAmount;
|
||||
|
||||
public Long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
public void setAccountId(Long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public Long getCompanyId() {
|
||||
return companyId;
|
||||
}
|
||||
|
||||
public void setCompanyId(Long companyId) {
|
||||
this.companyId = companyId;
|
||||
}
|
||||
|
||||
public BigDecimal getFirstLegAmount() {
|
||||
return firstLegAmount;
|
||||
}
|
||||
|
||||
public void setFirstLegAmount(BigDecimal firstLegAmount) {
|
||||
this.firstLegAmount = firstLegAmount;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue