Merge branch 'dev' into dev_xml-test

This commit is contained in:
Ivan Nikolaev-Axenov 2026-04-30 17:37:30 +03:00
commit c6eb287095
43 changed files with 837 additions and 224 deletions

View file

@ -72,7 +72,7 @@ public class BankAccountValidationConfig {
AccountError.DictionaryNotFound, AccountError.DictionaryNotFound,
false), false),
ValidateSymbolsRule.notRequired("БИК", ValidateSymbolsRule.notRequired("БИК",
BankAccountUpdateRequest::getBankIdentificationCode, BankAccountNewRequest::getBankIdentificationCode,
AccountError.BadSymbol), AccountError.BadSymbol),
ValidateSymbolsRule.required("Наименование компании", ValidateSymbolsRule.required("Наименование компании",
BankAccountNewRequest::getBankName, BankAccountNewRequest::getBankName,

View file

@ -192,7 +192,7 @@ public class RegistryController extends AbstractQueueController {
@ApiOperation(value = "Вывод средств с регистра невыясненных активов") @ApiOperation(value = "Вывод средств с регистра невыясненных активов")
@ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)}) @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)})
@RequestMapping(value = "/withdrawalFunds", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @RequestMapping(value = "/withdrawalFunds", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody @ResponseBody
public CudResponse dmauRegistryNew( public CudResponse dmauRegistryNew(
@ApiParam(value = "Параметры команды в JSON формате.", required = true) @ApiParam(value = "Параметры команды в JSON формате.", required = true)

View file

@ -12,9 +12,12 @@ public class RiskParameterNewAction implements IAction<RiskParameterNewRequest>
@ApiModelProperty(value = "Курс.", example = "1234") @ApiModelProperty(value = "Курс.", example = "1234")
@JsonProperty @JsonProperty
private BigDecimal value; private BigDecimal value;
@ApiModelProperty(value = "Ставка сопряженной валюты.", example = "1234") @ApiModelProperty(value = "Ставка переноса при нехватке рублей.", example = "1234")
@JsonProperty @JsonProperty
private BigDecimal quoteValue; private BigDecimal quoteValue;
@ApiModelProperty(value = "Ставка переноса при нехватке валюты.", example = "1234")
@JsonProperty
private BigDecimal penaltyValue;
@ApiModelProperty(value = "Код расчёта.", example = "1234") @ApiModelProperty(value = "Код расчёта.", example = "1234")
@JsonProperty @JsonProperty
private String settlementType; private String settlementType;
@ -30,6 +33,7 @@ public class RiskParameterNewAction implements IAction<RiskParameterNewRequest>
RiskParameterNewRequest request = new RiskParameterNewRequest(); RiskParameterNewRequest request = new RiskParameterNewRequest();
request.setValue(this.value); request.setValue(this.value);
request.setQuoteValue(this.quoteValue); request.setQuoteValue(this.quoteValue);
request.setPenaltyValue(this.penaltyValue);
request.setSettlementType(this.settlementType); request.setSettlementType(this.settlementType);
request.setCurrencyPairId(this.currencyPairId); request.setCurrencyPairId(this.currencyPairId);
request.setWorkflowStatus(this.workflowStatus); request.setWorkflowStatus(this.workflowStatus);
@ -73,4 +77,8 @@ public class RiskParameterNewAction implements IAction<RiskParameterNewRequest>
public void setWorkflowStatus(String workflowStatus) { public void setWorkflowStatus(String workflowStatus) {
this.workflowStatus = workflowStatus; this.workflowStatus = workflowStatus;
} }
public void setPenaltyValue(BigDecimal penaltyValue) {
this.penaltyValue = penaltyValue;
}
} }

View file

@ -15,9 +15,12 @@ public class RiskParameterUpdateAction implements IAction<RiskParameterUpdateReq
@ApiModelProperty(value = "Курс.", example = "1234") @ApiModelProperty(value = "Курс.", example = "1234")
@JsonProperty @JsonProperty
private BigDecimal value; private BigDecimal value;
@ApiModelProperty(value = "Ставка сопряженной валюты.", example = "1234") @ApiModelProperty(value = "Ставка переноса при нехватке рублей.", example = "1234")
@JsonProperty @JsonProperty
private BigDecimal quoteValue; private BigDecimal quoteValue;
@ApiModelProperty(value = "Ставка переноса при нехватке валюты.", example = "1234")
@JsonProperty
private BigDecimal penaltyValue;
@ApiModelProperty(value = "Статус.", example = "1234") @ApiModelProperty(value = "Статус.", example = "1234")
@JsonProperty @JsonProperty
private String workflowStatus; private String workflowStatus;
@ -28,6 +31,7 @@ public class RiskParameterUpdateAction implements IAction<RiskParameterUpdateReq
request.setId(this.id); request.setId(this.id);
request.setValue(this.value); request.setValue(this.value);
request.setQuoteValue(this.quoteValue); request.setQuoteValue(this.quoteValue);
request.setPenaltyValue(this.penaltyValue);
request.setWorkflowStatus(this.workflowStatus); request.setWorkflowStatus(this.workflowStatus);
return request; return request;
} }
@ -61,4 +65,12 @@ public class RiskParameterUpdateAction implements IAction<RiskParameterUpdateReq
public void setWorkflowStatus(String workflowStatus) { public void setWorkflowStatus(String workflowStatus) {
this.workflowStatus = workflowStatus; this.workflowStatus = workflowStatus;
} }
public void setQuoteValue(BigDecimal quoteValue) {
this.quoteValue = quoteValue;
}
public void setPenaltyValue(BigDecimal penaltyValue) {
this.penaltyValue = penaltyValue;
}
} }

View file

@ -1,6 +1,6 @@
{ {
"version": "3.23.295.165", "version": "3.23.295.169",
"enums": { "enums": {
@ -6389,17 +6389,6 @@
"name": "Отправка заявок в Торговую систему", "name": "Отправка заявок в Торговую систему",
"fields": []
}
,
{"method":"post",
"destination": "ORST",
"group": "Обмен с Торговой системой",
"name": "Получение статуса заявок из Торговой системы",
"fields": [] "fields": []
} }
] ]
@ -11089,9 +11078,13 @@
{"code": "value", {"code": "value",
"name": "Ставка","shortname": "Ставка","type": 10 "name": "Ставка","shortname": "Ставка","type": 10
} }
,
{"code": "penaltyValue",
"name": "Ставка переноса при нехватке валюты","shortname": "Ставка переноса при нехватке валюты","type": 10
}
, ,
{"code": "quoteValue", {"code": "quoteValue",
"name": "Ставка сопряженной валюты","shortname": "Ставка сопряженной валюты","type": 10 "name": "Ставка переноса при нехватке рублей","shortname": "Ставка переноса при нехватке рублей","type": 10
} }
, ,
{"code": "workflowStatus", {"code": "workflowStatus",
@ -11103,7 +11096,7 @@
"name": "Добавление параметров риска", "name": "Добавление параметров риска",
"confirmation": "value,quoteValue,settlementType,currencyPairId,workflowStatus", "confirmation": "value,penaltyValue,quoteValue,settlementType,currencyPairId,workflowStatus",
"class": "ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterNewAction", "class": "ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterNewAction",
@ -11119,9 +11112,13 @@
{"code": "value", {"code": "value",
"name": "Ставка","shortname": "Ставка","type": 10 "name": "Ставка","shortname": "Ставка","type": 10
} }
,
{"code": "penaltyValue",
"name": "Ставка переноса при нехватке валюты","shortname": "Ставка переноса при нехватке валюты","type": 10
}
, ,
{"code": "quoteValue", {"code": "quoteValue",
"name": "Ставка сопряженной валюты","shortname": "Ставка сопряженной валюты","type": 10 "name": "Ставка переноса при нехватке рублей","shortname": "Ставка переноса при нехватке рублей","type": 10
} }
, ,
{"code": "workflowStatus", {"code": "workflowStatus",
@ -11134,7 +11131,7 @@
"name": "Изменение параметров риска", "name": "Изменение параметров риска",
"confirmation": "value,quoteValue,workflowStatus", "confirmation": "value,penaltyValue,quoteValue,workflowStatus",
"class": "ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterUpdateAction", "class": "ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterUpdateAction",
@ -11146,9 +11143,13 @@
{"code": "value", {"code": "value",
"name": "Ставка","shortname": "Ставка","type": 10 "name": "Ставка","shortname": "Ставка","type": 10
} }
,
{"code": "penaltyValue",
"name": "Ставка переноса при нехватке валюты","shortname": "Ставка переноса при нехватке валюты","type": 10
}
, ,
{"code": "quoteValue", {"code": "quoteValue",
"name": "Ставка сопряженной валюты","shortname": "Ставка сопряженной валюты","type": 10 "name": "Ставка переноса при нехватке рублей","shortname": "Ставка переноса при нехватке рублей","type": 10
} }
, ,
{"code": "workflowStatus", {"code": "workflowStatus",
@ -11743,7 +11744,7 @@
} }
, ,
{"code": "market", {"code": "market",
"type": 12,"name": "Код режима торгов","shortname": "Режим торгов","visible": true,"searchable": true,"sortable": true,"link": "market","linkCode": "code" "type": 12,"name": "Код режима торгов","shortname": "Режим торгов","visible": true,"searchable": true,"sortable": true
} }
, ,
{"code": "status", {"code": "status",
@ -11831,7 +11832,7 @@
} }
, ,
{"code": "market", {"code": "market",
"type": 12,"name": "Код режима торгов","shortname": "Режим торгов","link": "market","linkCode": "code","enabled": false,"required": true "type": 12,"name": "Код режима торгов","shortname": "Режим торгов","enabled": false,"required": true
} }
, ,
{"code": "price", {"code": "price",

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--?xml-stylesheet type="text/xsl" href="\..\corp-reports\src\data\meta\meta.server.xslt"?--> <!--?xml-stylesheet type="text/xsl" href="\..\corp-reports\src\data\meta\meta.server.xslt"?-->
<meta version="3.23.295.165"> <meta version="3.23.295.169">
<!-- _xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" _xsi:noNamespaceSchemaLocation="file:///E:/d/projects/meta/from/meta.xsd" --> <!-- _xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" _xsi:noNamespaceSchemaLocation="file:///E:/d/projects/meta/from/meta.xsd" -->
<!--Здесь словари--> <!--Здесь словари-->
<enums> <enums>
@ -1478,8 +1478,6 @@
</post> </post>
<post destination="SDOR" group="Обмен с Торговой системой" name="Отправка заявок в Торговую систему"> <post destination="SDOR" group="Обмен с Торговой системой" name="Отправка заявок в Торговую систему">
</post> </post>
<post destination="ORST" group="Обмен с Торговой системой" name="Получение статуса заявок из Торговой системы">
</post>
</actions> </actions>
</launcher> </launcher>
<session name="Клиринговые сессии" destination="sessions" historyDestination="history" class="ru.clearing.classes.statics.data.misc.Session" logUpdates="true" table="session"> <session name="Клиринговые сессии" destination="sessions" historyDestination="history" class="ru.clearing.classes.statics.data.misc.Session" logUpdates="true" table="session">
@ -2570,20 +2568,23 @@
<currencyPairId name="Идентификатор валютной пары" shortname="Валютная пара" type="1"/> <currencyPairId name="Идентификатор валютной пары" shortname="Валютная пара" type="1"/>
<settlementType name="Код расчета" shortname="Код расчета" type="2" length="4"/> <settlementType name="Код расчета" shortname="Код расчета" type="2" length="4"/>
<value name="Ставка" shortname="Ставка" type="10"/> <value name="Ставка" shortname="Ставка" type="10"/>
<quoteValue name="Ставка сопряженной валюты" shortname="Ставка сопряженной валюты" type="10"/> <penaltyValue name="Ставка переноса при нехватке валюты" shortname="Ставка переноса при нехватке валюты" type="10"/>
<quoteValue name="Ставка переноса при нехватке рублей" shortname="Ставка переноса при нехватке рублей" type="10"/>
<workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/> <workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/>
<actions> <actions>
<post name="Добавление параметров риска" confirmation="value,quoteValue,settlementType,currencyPairId,workflowStatus" class="ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterNewAction"> <post name="Добавление параметров риска" confirmation="value,penaltyValue,quoteValue,settlementType,currencyPairId,workflowStatus" class="ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterNewAction">
<currencyPairId name="Идентификатор валютной пары" shortname="Валютная пара" type="1" link="currencyPair" linkCode="code" linkKeyCode="id"/> <currencyPairId name="Идентификатор валютной пары" shortname="Валютная пара" type="1" link="currencyPair" linkCode="code" linkKeyCode="id"/>
<settlementType name="Код расчёта" shortname="Код расчёта" type="12" link="currencySettlementType" linkCode="code"/> <settlementType name="Код расчёта" shortname="Код расчёта" type="12" link="currencySettlementType" linkCode="code"/>
<value name="Ставка" shortname="Ставка" type="10"/> <value name="Ставка" shortname="Ставка" type="10"/>
<quoteValue name="Ставка сопряженной валюты" shortname="Ставка сопряженной валюты" type="10"/> <penaltyValue name="Ставка переноса при нехватке валюты" shortname="Ставка переноса при нехватке валюты" type="10"/>
<quoteValue name="Ставка переноса при нехватке рублей" shortname="Ставка переноса при нехватке рублей" type="10"/>
<workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/> <workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/>
</post> </post>
<put name="Изменение параметров риска" confirmation="value,quoteValue,workflowStatus" class="ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterUpdateAction"> <put name="Изменение параметров риска" confirmation="value,penaltyValue,quoteValue,workflowStatus" class="ru.spcex.clearing.backendapi.controller.request.cud.securities.RiskParameterUpdateAction">
<id type="1" name="Идентификатор записи" shortname="ID" link="riskParameter" linkCode="id" required="true"/> <id type="1" name="Идентификатор записи" shortname="ID" link="riskParameter" linkCode="id" required="true"/>
<value name="Ставка" shortname="Ставка" type="10"/> <value name="Ставка" shortname="Ставка" type="10"/>
<quoteValue name="Ставка сопряженной валюты" shortname="Ставка сопряженной валюты" type="10"/> <penaltyValue name="Ставка переноса при нехватке валюты" shortname="Ставка переноса при нехватке валюты" type="10"/>
<quoteValue name="Ставка переноса при нехватке рублей" shortname="Ставка переноса при нехватке рублей" type="10"/>
<workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/> <workflowStatus name="Статус" shortname="Статус" type="2" length="4" link="workflowStatus"/>
</put> </put>
</actions> </actions>
@ -2726,7 +2727,7 @@
<account type="2" length="50" name="Торговый счет" shortname="Счет" searchable="true" sortable="true" visible="true"/> <account type="2" length="50" name="Торговый счет" shortname="Счет" searchable="true" sortable="true" visible="true"/>
<companyId type="1" dbname="Идентификатор участника" name="Наименование участника" shortname="Участник" searchable="true" sortable="true" visible="true" link="company" linkCode="shortName"/> <companyId type="1" dbname="Идентификатор участника" name="Наименование участника" shortname="Участник" searchable="true" sortable="true" visible="true" link="company" linkCode="shortName"/>
<tradingCode type="2" length="255" name="Код участника торгов" shortname="Код УТ" searchable="true" sortable="true" visible="true"/> <tradingCode type="2" length="255" name="Код участника торгов" shortname="Код УТ" searchable="true" sortable="true" visible="true"/>
<market type="12" name="Код режима торгов" shortname="Режим торгов" visible="true" searchable="true" sortable="true" link="market" linkCode="code"/> <market type="12" name="Код режима торгов" shortname="Режим торгов" visible="true" searchable="true" sortable="true"/>
<status type="2" length="4" name="Статус заявки" shortname="Статус" link="orderStatus" searchable="true" sortable="true" visible="true"/> <status type="2" length="4" name="Статус заявки" shortname="Статус" link="orderStatus" searchable="true" sortable="true" visible="true"/>
<price type="10" name="Цена заявки" shortname="Цена" searchable="true" sortable="true" visible="true"/> <price type="10" name="Цена заявки" shortname="Цена" searchable="true" sortable="true" visible="true"/>
<quantityLot type="3" name="Объем заявки в лотах" shortname="Объем заявки в лотах" searchable="true" sortable="true" visible="false"/> <quantityLot type="3" name="Объем заявки в лотах" shortname="Объем заявки в лотах" searchable="true" sortable="true" visible="false"/>
@ -2748,7 +2749,7 @@
<put name="Изменение заявки" class="ru.spcex.clearing.backendapi.controller.request.cud.order.OrderCurrencyUpdateAction"> <put name="Изменение заявки" class="ru.spcex.clearing.backendapi.controller.request.cud.order.OrderCurrencyUpdateAction">
<id type="1" name="Идентификатор записи" shortname="ID" link="orderCurrency" linkCode="id" required="true"/> <id type="1" name="Идентификатор записи" shortname="ID" link="orderCurrency" linkCode="id" required="true"/>
<securityId type="1" name="Наименование инструмента" shortname="Инструмент" link="security" linkCode="shortName" enabled="false" required="true"/> <securityId type="1" name="Наименование инструмента" shortname="Инструмент" link="security" linkCode="shortName" enabled="false" required="true"/>
<market type="12" name="Код режима торгов" shortname="Режим торгов" link="market" linkCode="code" enabled="false" required="true"/> <market type="12" name="Код режима торгов" shortname="Режим торгов" enabled="false" required="true"/>
<price type="10" name="Цена заявки" shortname="Цена" required="true"/> <price type="10" name="Цена заявки" shortname="Цена" required="true"/>
<quantityLot type="3" name="Объем заявки в лотах" shortname="Объем заявки" required="true"/> <quantityLot type="3" name="Объем заявки в лотах" shortname="Объем заявки" required="true"/>
<side type="12" name="Направление заявки" shortname="Направление" required="true" link="side"/> <side type="12" name="Направление заявки" shortname="Направление" required="true" link="side"/>

View file

@ -10,6 +10,7 @@ public class RiskParameter extends BusinessObject {
private Long currencyPairId; private Long currencyPairId;
private String workflowStatus; private String workflowStatus;
private BigDecimal quoteValue; private BigDecimal quoteValue;
private BigDecimal penaltyValue;
public BigDecimal getValue() { public BigDecimal getValue() {
return value; return value;
@ -58,4 +59,12 @@ public class RiskParameter extends BusinessObject {
public void setQuoteValue(BigDecimal quoteValue) { public void setQuoteValue(BigDecimal quoteValue) {
this.quoteValue = quoteValue; this.quoteValue = quoteValue;
} }
public BigDecimal getPenaltyValue() {
return penaltyValue;
}
public void setPenaltyValue(BigDecimal penaltyValue) {
this.penaltyValue = penaltyValue;
}
} }

View file

@ -1,5 +1,6 @@
package ru.spcex.clearing.config; package ru.spcex.clearing.config;
import java.util.Optional;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -41,11 +42,14 @@ import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest; 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.payment.PIClearingOutbondActionNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.DmauRegistryRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.IdentificationFundsRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.IdentificationFundsRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeStatusExtractRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeStatusExtractRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryReturnDepositRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistrySplitDepositActionRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistrySplitDepositActionRequest;
import ru.spcex.clearing.service.AssetTrio;
import ru.spcex.clearing.service.registry.AssetTBFProcessing;
import ru.spcex.clearing.service.validation.AnltAccLoad; import ru.spcex.clearing.service.validation.AnltAccLoad;
import ru.spcex.clearing.service.validation.ClrngValidationStored; import ru.spcex.clearing.service.validation.ClrngValidationStored;
import ru.spcex.clearing.service.validation.CompanyByTradingCodeValidationRule; import ru.spcex.clearing.service.validation.CompanyByTradingCodeValidationRule;
@ -68,18 +72,27 @@ import ru.spcex.clearing.service.validation.SecurityBySecurityCodeValidationRule
import ru.spcex.clearing.service.validation.SessionTerminationValidationRule; import ru.spcex.clearing.service.validation.SessionTerminationValidationRule;
import ru.spcex.clearing.service.validation.SplitDepositRequestValidationRule; import ru.spcex.clearing.service.validation.SplitDepositRequestValidationRule;
import ru.spcex.clearing.service.validation.StatusExtractValidationRule; import ru.spcex.clearing.service.validation.StatusExtractValidationRule;
import ru.spcex.clearing.service.validation.WithdrawalFundsValidationRule;
import ru.spcex.clearing.service.validation.WithdrawalFundsValidationStore;
import ru.spcex.clearing.util.security.UserRoleVerification; import ru.spcex.clearing.util.security.UserRoleVerification;
import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule; import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule;
import ru.spcex.clearing.validation.common.rules.FieldRequiredRule; import ru.spcex.clearing.validation.common.rules.FieldRequiredRule;
import ru.spcex.clearing.validation.common.rules.IdPresentRule; import ru.spcex.clearing.validation.common.rules.IdPresentRule;
import ru.spcex.platform.enumeration.ClearingCategory; import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.enumeration.CompanySymbol;
import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.imdg.api.predicate.specific.IdEqualPredicate;
import ru.spcex.platform.imdg.validation.ImdgValidationContext; import ru.spcex.platform.imdg.validation.ImdgValidationContext;
import ru.spcex.platform.imdg.validation.LogPrefixId; import ru.spcex.platform.imdg.validation.LogPrefixId;
import ru.spcex.platform.imdg.validation.rule.CurrencyCodeValidationRule; import ru.spcex.platform.imdg.validation.rule.CurrencyCodeValidationRule;
import ru.spcex.platform.imdg.validation.rule.PresentById; import ru.spcex.platform.imdg.validation.rule.PresentById;
import ru.spcex.platform.imdg.validation.rule.PresentByPredicate;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.enumeration.IMessageResolver; import ru.spcex.platform.utils.enumeration.IMessageResolver;
import ru.spcex.platform.utils.validation.IValidationRule;
import ru.spcex.platform.utils.validation.IValidator; import ru.spcex.platform.utils.validation.IValidator;
import ru.spcex.platform.utils.validation.ValidatorImpl; import ru.spcex.platform.utils.validation.ValidatorImpl;
@ -113,8 +126,9 @@ public class ValidationConfig {
Imdg<PaymentInstruction> pmtImdg; Imdg<PaymentInstruction> pmtImdg;
Imdg<ClearingMemberCategory> ctgrImdg; Imdg<ClearingMemberCategory> ctgrImdg;
ImdgProvider imdgProvider; ImdgProvider imdgProvider;
AssetTBFProcessing assetsMng;
public ValidationConfig(ImdgProvider imdgProvider) { public ValidationConfig(ImdgProvider imdgProvider, AssetTBFProcessing assetsMng) {
this.imdgRelation = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); this.imdgRelation = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class);
this.imdgCompany = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); this.imdgCompany = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
this.imdgCurrency = imdgProvider.getImdg(IMDGDistributedNames.Map_Currency, Currency.class); this.imdgCurrency = imdgProvider.getImdg(IMDGDistributedNames.Map_Currency, Currency.class);
@ -142,6 +156,7 @@ public class ValidationConfig {
this.currencyPairSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairSecurity, CurrencyPairSecurity.class); this.currencyPairSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_CurrencyPairSecurity, CurrencyPairSecurity.class);
this.digitalCertificateSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_DigitalCertificateSecurity, DigitalCertificateSecurity.class); this.digitalCertificateSecurityImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_DigitalCertificateSecurity, DigitalCertificateSecurity.class);
this.imdgProvider = imdgProvider; this.imdgProvider = imdgProvider;
this.assetsMng = assetsMng;
} }
//@Bean("adminRoleVerification") //@Bean("adminRoleVerification")
@ -399,6 +414,80 @@ public class ValidationConfig {
}; };
} }
@Bean("withdrawalFundsValidator")
public Function<DmauRegistryRequest, IValidator> withdrawalFundsValidator() {
return identificationFundsRequest -> {
ImdgValidationContext<DmauRegistryRequest> context = new ImdgValidationContext<>();
context.setValidatedObject(identificationFundsRequest);
context.addImdg(IMDGDistributedNames.Map_Account, imdgAccount);
context.addImdg(IMDGDistributedNames.Map_Company, imdgCompany);
context.addImdg(IMDGDistributedNames.Map_CompanySymbols, imdgCompanySymbols);
context.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, imdgTradingClearingRegistry);
context.addImdg(IMDGDistributedNames.Map_TradingClearingRegistryList, imdgTradingClearingRegistryList);
context.setLogPrefix(dmauRegistryRequest ->
"[debitCorrAccount:%s]".formatted(dmauRegistryRequest.getDebitCorrAccount()
));
ImdgPredicateBuilder cmpSymbPb = imdgCompanySymbols.predicateBuilder();
var cmpSymbCheck = PresentByPredicate.builder(
(Function<DmauRegistryRequest, ImdgPredicate>) req -> cmpSymbPb.and(
cmpSymbPb.equals("companyId", req.getSenderId()),
cmpSymbPb.equals("companySymbol", CompanySymbol.BIC.getKey())
))
.mapName(IMDGDistributedNames.Map_CompanySymbols)
.errorProvider(req -> new EnumMessage(ClearingError.RecordNotFound, "companySymbol"))
.store(WithdrawalFundsValidationStore.CompanySymbol)
.build()
;
ImdgPredicateBuilder cmpPb = imdgCompany.predicateBuilder();
var cmpCheck = PresentByPredicate.builder(new IdEqualPredicate<>(
cmpPb, DmauRegistryRequest::getSenderId
))
.mapName(IMDGDistributedNames.Map_Company)
.errorProvider(req -> new EnumMessage(ClearingError.RecordNotFound, "company"))
.store(WithdrawalFundsValidationStore.Company)
.build();
ImdgPredicateBuilder accPb = imdgAccount.predicateBuilder();
var accCheck = PresentByPredicate.builder(new IdEqualPredicate<>(
accPb, DmauRegistryRequest::getCreditLegAccountId
))
.mapName(IMDGDistributedNames.Map_Account)
.errorProvider(req -> new EnumMessage(ClearingError.RecordNotFound, "account"))
.store(WithdrawalFundsValidationStore.Account)
.build();
var assetPresent = new IValidationRule<ImdgValidationContext<DmauRegistryRequest>>() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<DmauRegistryRequest> ctx) {
Company cmp = ctx.getStoredObject(WithdrawalFundsValidationStore.Company);
Account acc = ctx.getStoredObject(WithdrawalFundsValidationStore.Account);
Optional<AssetTrio> assetsTrio = assetsMng.searchMoneyByAccAndCompany(cmp.getId(), acc.getId());
if (assetsTrio.isEmpty()) {
return of(ClearingError.RecordNotFound, "AM_F/AM_B/AM_T");
}
ctx.storeObject(WithdrawalFundsValidationStore.AssetTrio, assetsTrio.get());
return empty();
}
@Override
public String ruleName() {
return "assets-present";
}
};
return new ValidatorImpl<>(context,
WithdrawalFundsValidationRule.Fields,
cmpSymbCheck,
cmpCheck,
accCheck,
assetPresent
);
};
}
@Bean("splitDepositValidator") @Bean("splitDepositValidator")
public Function<RegistrySplitDepositActionRequest, IValidator> splitDepositValidator() { public Function<RegistrySplitDepositActionRequest, IValidator> splitDepositValidator() {
return refundDateRequest -> { return refundDateRequest -> {

View file

@ -6,6 +6,7 @@ public enum ClearingMessage implements IEnumKey {
WithdrawalFreeFromTkr("payment-instruction.purpose.withdrawal.tcr"), WithdrawalFreeFromTkr("payment-instruction.purpose.withdrawal.tcr"),
WithdrawalOfFunds("payment-instruction.purpose.withdrawal"), WithdrawalOfFunds("payment-instruction.purpose.withdrawal"),
urgent("sdf54.urgent"), urgent("sdf54.urgent"),
withdrawalOfFundsDmau("withdrawal.funds.dmau"),
; ;
private final String key; private final String key;

View file

@ -23,6 +23,7 @@ import ru.spcex.clearing.platform.messaging.domain.cud.clearing.SessionContinueE
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.AssetOperationApprovalRequest; import ru.spcex.clearing.platform.messaging.domain.cud.gateway.AssetOperationApprovalRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.payment.PIClearingOutbondActionNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.payment.PIClearingOutbondActionNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.DmauRegistryRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.IdentificationFundsRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.IdentificationFundsRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeRefundDateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeStatusExtractRequest; import ru.spcex.clearing.platform.messaging.domain.cud.registry.RegistryChangeStatusExtractRequest;
@ -40,6 +41,7 @@ import ru.spcex.clearing.service.execution.ExecutionFondComponent;
import ru.spcex.clearing.service.executors.Sdf06Executor; import ru.spcex.clearing.service.executors.Sdf06Executor;
import ru.spcex.clearing.service.executors.Sdf10Executor; import ru.spcex.clearing.service.executors.Sdf10Executor;
import ru.spcex.clearing.service.payment.PaymentInstructionOutboundService; import ru.spcex.clearing.service.payment.PaymentInstructionOutboundService;
import ru.spcex.clearing.service.payment.WithdrawalFundsService;
import ru.spcex.clearing.session.stage.SessionTerminator; import ru.spcex.clearing.session.stage.SessionTerminator;
import ru.spcex.clearing.session.stage.TaskType; import ru.spcex.clearing.session.stage.TaskType;
import ru.spcex.clearing.session.stage.impl.BalanceRevise; import ru.spcex.clearing.session.stage.impl.BalanceRevise;
@ -65,6 +67,7 @@ public class EventsReceiver extends QueueConsumerV2 implements InitializingBean
private final StatementServiceV2 statementService; private final StatementServiceV2 statementService;
private final SessionTerminator sessionTerminator; private final SessionTerminator sessionTerminator;
private final PaymentInstructionOutboundService pmtOutboundService; private final PaymentInstructionOutboundService pmtOutboundService;
private final WithdrawalFundsService withdrawalFundsService;
private final ExecutionCurrencyComponent execCurrUpload; private final ExecutionCurrencyComponent execCurrUpload;
private final ExecutionDepositComponent execDepUpload; private final ExecutionDepositComponent execDepUpload;
private final ExecutionFondComponent execFondUpload; private final ExecutionFondComponent execFondUpload;
@ -81,7 +84,7 @@ public class EventsReceiver extends QueueConsumerV2 implements InitializingBean
Sdf05Sender sdf05Sender, Sdf05Sender sdf05Sender,
StatementServiceV2 statementService, StatementServiceV2 statementService,
SessionTerminator sessionTerminator, SessionTerminator sessionTerminator,
PaymentInstructionOutboundService pmtOutboundService, PaymentInstructionOutboundService pmtOutboundService, WithdrawalFundsService withdrawalFundsService,
ExecutionCurrencyComponent execCurrUpload, ExecutionCurrencyComponent execCurrUpload,
ExecutionDepositComponent execDepUpload, ExecutionDepositComponent execDepUpload,
ExecutionFondComponent execFondUpload) { ExecutionFondComponent execFondUpload) {
@ -98,6 +101,7 @@ public class EventsReceiver extends QueueConsumerV2 implements InitializingBean
this.statementService = statementService; this.statementService = statementService;
this.sessionTerminator = sessionTerminator; this.sessionTerminator = sessionTerminator;
this.pmtOutboundService = pmtOutboundService; this.pmtOutboundService = pmtOutboundService;
this.withdrawalFundsService = withdrawalFundsService;
this.execCurrUpload = execCurrUpload; this.execCurrUpload = execCurrUpload;
this.execDepUpload = execDepUpload; this.execDepUpload = execDepUpload;
this.execFondUpload = execFondUpload; this.execFondUpload = execFondUpload;
@ -144,6 +148,9 @@ public class EventsReceiver extends QueueConsumerV2 implements InitializingBean
callback(PIClearingOutbondActionNewRequest.class) callback(PIClearingOutbondActionNewRequest.class)
.setFunction(pmtOutboundService::sendOutBoundPayment) .setFunction(pmtOutboundService::sendOutBoundPayment)
.forDestination(Consts.PAYMENT_INSTRUCTION_CLEARING_OUTBOUND_ACTION, callbacks::put); .forDestination(Consts.PAYMENT_INSTRUCTION_CLEARING_OUTBOUND_ACTION, callbacks::put);
callback(DmauRegistryRequest.class)
.setFunction(withdrawalFundsService::withdrawalFunds)
.forDestination(Consts.REGISTRY_DMAU_CREATE_ACTION, callbacks::put);
callback(StatementRequest.class) callback(StatementRequest.class)
.setConsumer(statementService::processReq) .setConsumer(statementService::processReq)
.forDestination(Consts.STATEMENT_PROCESS, callbacks::put); .forDestination(Consts.STATEMENT_PROCESS, callbacks::put);

View file

@ -124,9 +124,11 @@ public class Sdf54Creator {
sDf54.setKpp_deb(kppDeb); sDf54.setKpp_deb(kppDeb);
} }
Account accDebAcc = accountImdg.getSingleObjectByID(pmt.getDebitLeg_accountId()); if (pmt.getDebitLeg_accountId() != null) {
if (accDebAcc == null || !AccountStatus.ACTIVE.equalsByKey(accDebAcc.getStatus())) { Account accDebAcc = accountImdg.getSingleObjectByID(pmt.getDebitLeg_accountId());
log.warn("paymentInstruction[{}].DebitLeg_accountId={} account not exist or not active.", pmt.getId(), pmt.getDebitLeg_accountId()); if (accDebAcc == null || !AccountStatus.ACTIVE.equalsByKey(accDebAcc.getStatus())) {
log.warn("paymentInstruction[{}].DebitLeg_accountId={} account not exist or not active.", pmt.getId(), pmt.getDebitLeg_accountId());
}
} }
BankAccount bnkAcc = bankAccountImdg.getFirstObjectBySQL("accountId = %d".formatted(pmt.getDebitLeg_accountId())); BankAccount bnkAcc = bankAccountImdg.getFirstObjectBySQL("accountId = %d".formatted(pmt.getDebitLeg_accountId()));
if (bnkAcc != null) { if (bnkAcc != null) {

View file

@ -69,7 +69,11 @@ public class Sdf55Executor {
pmt.setUpdated(Instant.now()); pmt.setUpdated(Instant.now());
pmtImdg.update(pmt); pmtImdg.update(pmt);
String curr = TextUtil.isEmpty(sDf55.getPay_val()) ? CurrencyCode.RUB.getKey() : sDf55.getPay_val(); String curr = TextUtil.isEmpty(sDf55.getPay_val()) ? CurrencyCode.RUB.getKey() : sDf55.getPay_val();
dmiService.setContract(tcr.getId(), curr, sDf54.getDocnm_ref(), sDf55.getDocnmprev()); if (tcr != null) {
dmiService.setContract(tcr.getId(), curr, sDf54.getDocnm_ref(), sDf55.getDocnmprev());
} else {
dmiService.setContractByAcc(pmt.getCreditLeg_accountId(), curr, sDf54.getDocnm_ref(), sDf55.getDocnmprev());
}
assets.process(assetTrio.a__b(), assets.process(assetTrio.a__b(),
assetTrio.a__t(), assetTrio.a__t(),
assetTrio.a__f(), assetTrio.a__f(),

View file

@ -453,6 +453,7 @@ public class Sdf57Executor extends AbstractExecutor<SDf57> {
Company cmp = stmtCmpAcc.company(); Company cmp = stmtCmpAcc.company();
Account acc = stmtCmpAcc.account(); Account acc = stmtCmpAcc.account();
Optional<AssetTrio> asts = createRegistryIfNeeded.apply(stmtCmpAcc); Optional<AssetTrio> asts = createRegistryIfNeeded.apply(stmtCmpAcc);
InOutDirection direction = IEnumKey.getEnumByKey(InOutDirection.class, stmt.getInOutDirection());
if (accountIsAnlt(acc)) { if (accountIsAnlt(acc)) {
AnltSearcher.AnltSearch searchResult = anltSearcher.loadByAnlt(stmt.getComment(), sdf57.getPay_val()); AnltSearcher.AnltSearch searchResult = anltSearcher.loadByAnlt(stmt.getComment(), sdf57.getPay_val());
if (searchResult.isFound()) { if (searchResult.isFound()) {
@ -475,7 +476,7 @@ public class Sdf57Executor extends AbstractExecutor<SDf57> {
assets.process(trio.a__b(), trio.a__t(), trio.a__f(), BigDecimal.ZERO); assets.process(trio.a__b(), trio.a__t(), trio.a__f(), BigDecimal.ZERO);
}); });
} }
} else if (InOutDirection.in.equals(IEnumKey.getEnumByKey(InOutDirection.class, stmt.getInOutDirection()))) { } else {
log.debug("stmt.id={} comment='{}' error: {}. Operating through DMAU registry", log.debug("stmt.id={} comment='{}' error: {}. Operating through DMAU registry",
stmt.getId(), stmt.getId(),
stmt.getComment(), stmt.getComment(),
@ -499,17 +500,14 @@ public class Sdf57Executor extends AbstractExecutor<SDf57> {
.build(); .build();
newDmau.setRegistryUnit(RegistryUnit.U.getKey()); newDmau.setRegistryUnit(RegistryUnit.U.getKey());
newDmau.setRegistryCode(RegistryUtil.clearingCode(newDmau)); newDmau.setRegistryCode(RegistryUtil.clearingCode(newDmau));
newDmau.setBalance(safeBD(stmt.getAmount())); updateDmauReg(stmt, newDmau);
registryImdg.insert(newDmau); registryImdg.insert(newDmau);
log.debug("stmt.id={} created DMAU.id={}", stmt.getId(), newDmau.getId()); log.debug("stmt.id={} created DMAU.id={}", stmt.getId(), newDmau.getId());
}); });
//DMAU подразумевает увеличение остатка поля balance и credit registry.code=DMAU //DMAU подразумевает увеличение остатка поля balance и credit registry.code=DMAU
String genTime = formatInstant(sdf57.getGenerationTime(), readableDateTime); String genTime = formatInstant(sdf57.getGenerationTime(), readableDateTime);
notification.sendNotification(ObjectType.rgst, String ntfMsg = dmauNotificationText(direction, genTime, sdf57);
("Зачисление средств на регистр невыясненных денежных средств. " notification.sendNotification(ObjectType.rgst, ntfMsg, Priority.HIGH);
+ "Дата и время поступления: %s. "
+ "ID платежного поручения: %d").formatted(genTime, sdf57.getDbfId()),
Priority.HIGH);
} }
} else if (OperationStatus.Executed.equalsByKey(stmt.getOperationStatus())) { } else if (OperationStatus.Executed.equalsByKey(stmt.getOperationStatus())) {
if (asts.isPresent()) { if (asts.isPresent()) {
@ -555,6 +553,25 @@ public class Sdf57Executor extends AbstractExecutor<SDf57> {
return result; return result;
} }
private String dmauNotificationText(InOutDirection direction, String genTime, SDf57 sdf57) {
if (direction == null) return "notification error: unknown direction null";
switch (direction) {
case in -> {
return ("Зачисление средств на регистр невыясненных денежных средств. "
+ "Дата и время поступления: %s. "
+ "ID платежного поручения: %d").formatted(genTime, sdf57.getDbfId());
}
case out -> {
return ("Списание средств с регистра невыясненных денежных средств %s. " +
"Дата и время списания: %s. " +
"Объем списания: %s").formatted(
sdf57.getAcc_deb(), genTime, sdf57.getSum_deb()
);
}
default -> {return "notification error: unknown direction " + direction;}
}
}
private boolean accountIsAnlt(Account account) { private boolean accountIsAnlt(Account account) {
if (account == null) return false; if (account == null) return false;
boolean anlt = AccountType.Anlt.getKey().equals(account.getAccountType()); boolean anlt = AccountType.Anlt.getKey().equals(account.getAccountType());

View file

@ -0,0 +1,194 @@
package ru.spcex.clearing.service.payment;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Optional;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.CompanySymbols;
import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.sdf.SDf54;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.messages.ClearingMessage;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
import ru.spcex.clearing.platform.messaging.domain.Consts;
import ru.spcex.clearing.platform.messaging.domain.cud.clearing.SdfClearingRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.DmauRegistryRequest;
import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.service.AssetTrio;
import ru.spcex.clearing.service.Sdf54Creator;
import ru.spcex.clearing.service.SpecifUtil;
import ru.spcex.clearing.service.registry.AssetTBFProcessing;
import ru.spcex.clearing.service.registry.DmiService;
import ru.spcex.clearing.service.validation.WithdrawalFundsValidationStore;
import ru.spcex.clearing.util.LocaleUtil;
import ru.spcex.clearing.util.security.UserRoleVerification;
import ru.spcex.clearing.util.services.RequestHelper;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.InOutDirection;
import ru.spcex.platform.enumeration.TransactionStatus;
import ru.spcex.platform.enumeration.UserRole;
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.enumeration.IMessageResolver;
import ru.spcex.platform.utils.localization.SpringPropertiesLocalizer;
import ru.spcex.platform.utils.log.ExceptionUtils;
import ru.spcex.platform.utils.text.TextUtil;
import ru.spcex.platform.utils.time.TimeUtil;
import ru.spcex.platform.utils.validation.IValidator;
@Service
public class WithdrawalFundsService {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Function<DmauRegistryRequest, IValidator> withdrawalFundsVal;
private final Sdf54Creator sdf54Creator;
private final Imdg<SDf54> sdf54Imdg;
private final Imdg<PaymentInstruction> pmtImdg;
private final Imdg<Registry> rgsImdg;
private final KafkaSender kafkaSender;
private final ImdgId idGen;
private final RequestHelper reqHelp;
private final UserRoleVerification rights;
private final IMessageResolver msgResolver;
private final SpringPropertiesLocalizer localizer;
private final AssetTBFProcessing assetsMng;
private final DmiService dmiService;
public WithdrawalFundsService(Function<DmauRegistryRequest, IValidator> withdrawalFundsVal, Sdf54Creator sdf54Creator, ImdgProvider imdgProvider, KafkaSender kafkaSender, RequestHelper reqHelp, UserRoleVerification rights, IMessageResolver msgResolver, SpringPropertiesLocalizer localizer, AssetTBFProcessing assetsMng, DmiService dmiService) {
this.withdrawalFundsVal = withdrawalFundsVal;
this.sdf54Creator = sdf54Creator;
this.kafkaSender = kafkaSender;
this.sdf54Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf54, SDf54.class);
this.pmtImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PaymentInstruction, PaymentInstruction.class);
this.rgsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.idGen = imdgProvider.getImdgIdGenerator();
this.reqHelp = reqHelp;
this.rights = rights;
this.msgResolver = msgResolver;
this.localizer = localizer;
this.assetsMng = assetsMng;
this.dmiService = dmiService;
}
public RequestInfoUpdate withdrawalFunds(BaseRequest<DmauRegistryRequest> req) {
if (!rights.userHasRole(req.getUserId(), UserRole.Admin)) {
return reqHelp.error(req.getId(), ClearingError.UserVerifyDenial);
}
DmauRegistryRequest payload = req.getRequestPayload();
IValidator validator = withdrawalFundsVal.apply(payload);
Optional<EnumMessage> err = validator.tillFirstError();
if (err.isPresent()) {
log.error("DmauRegistryRequest.id={} debitCorrAccount={} validation error: {}",
req.getId(),
payload != null ? payload.getDebitCorrAccount() : null,
msgResolver.resolve(err.get()));
return reqHelp.error(req.getId(), err.get());
}
log.info("DmauRegistryRequest.id={} debitCorrAccount={} validation passed", req.getId(), payload.getDebitCorrAccount());
CompanySymbols cmpSymb = validator.getStored(WithdrawalFundsValidationStore.CompanySymbol);
Company cmp = validator.getStored(WithdrawalFundsValidationStore.Company);
Account acc = validator.getStored(WithdrawalFundsValidationStore.Account);
AssetTrio assets = validator.getStored(WithdrawalFundsValidationStore.AssetTrio);
String curr = TextUtil.isEmpty(payload.getSecuritySymbol()) ? CurrencyCode.RUB.getKey() : payload.getSecuritySymbol();
Instant now = Instant.now();
LocalDate nowDate = TimeUtil.toLocalDate(now);
PaymentInstruction pmtIns = new PaymentInstruction();
pmtIns.setSenderId(payload.getSenderId());
pmtIns.setPayeeBic(cmpSymb.getCompanySymbolValue());
pmtIns.setAdresseeBic(payload.getDebitBIC());
pmtIns.setPayeeBankName(cmp.getShortName());
pmtIns.setAddresseeBankName(payload.getDebitBankName());
pmtIns.setPaymentDate(now);
pmtIns.setPaymentPurpose(extractPaymentPurpose(payload));
pmtIns.setSettlementDate(nowDate);
pmtIns.setCreditLeg_amount(payload.getAmount());
pmtIns.setDebitLeg_amount(payload.getAmount());
pmtIns.setCreditLeg_accountId(payload.getCreditLegAccountId());
pmtIns.setCreditLeg_account(acc.getAccount());
pmtIns.setDebit_csAccount(payload.getDebitCorrAccount());
pmtIns.setDebitLeg_account(payload.getDebitLegAccount());
pmtIns.setCreditLeg_direction(InOutDirection.out.getKey());
pmtIns.setDebitLeg_direction(InOutDirection.in.getKey());
pmtIns.setCreditLeg_currencyCode(payload.getSecuritySymbol());
pmtIns.setDebitLeg_currencyCode(payload.getSecuritySymbol());
pmtIns.setCreditLeg_securityId(payload.getSecurityId());
pmtIns.setDebitLeg_securityId(payload.getSecurityId());
pmtIns.setTransactionStatus(TransactionStatus.stld.getKey());
pmtIns.setCreated(now);
pmtIns.setClearingDate(nowDate);
pmtImdg.insert(pmtIns);
log.info("created PaymentInstruction.id={}", pmtIns.getId());
SDf54 sDf54;
try {
sDf54 = sdf54Creator.create(pmtIns);
} catch (IllegalStateException e) {
log.error(ExceptionUtils.getStackTrace(e));
return failedResponse(req, now, pmtIns);
}
sDf54.setId(idGen.nextId());
sDf54.setGenerationId(pmtIns.getId());
sDf54.setRbankcode(pmtIns.getAdresseeBic());
sDf54.setC_acc_cred(pmtIns.getDebit_csAccount());
SpecifUtil.fillSegmentsBy35Symbols(pmtIns.getAddresseeBankName(),
sDf54::setRbanknam1,
sDf54::setRbanknam2,
sDf54::setRbanknam3,
sDf54::setRbanknam4,
sDf54::setRbanknam5);
SpecifUtil.fillSegmentsBy35Symbols(payload.getDebitCompanyName(),
sDf54::setRclientn1,
sDf54::setRclientn2,
sDf54::setRclientn3,
sDf54::setRclientn4
);
sDf54.setInn_cred(payload.getDebitINN());
sDf54.setKpp_cred(payload.getDebitKPP());
sdf54Imdg.insert(sDf54);
log.info("created sdf54.id={}", sDf54.getId());
Registry d__i = dmiService.createDmi(payload.getAmount().negate(), assets.a__t());
d__i.setComment(sDf54.getDocnm_ref());
rgsImdg.insert(d__i);
log.debug("created {}.id={}; recalculating assests...", d__i.getRegistryCode(), d__i.getId());
assetsMng.process(assets.a__b(), assets.a__t(), assets.a__f(), BigDecimal.ZERO);
SdfClearingRequest exp = new SdfClearingRequest();
exp.setGroupId(sDf54.getGenerationId());
kafkaSender.sendRequestToQueue(Consts.SDF54_PROCESS, exp);
return reqHelp.success(req.getId());
}
private RequestInfoUpdate failedResponse(BaseRequest<DmauRegistryRequest> req,
Instant now,
PaymentInstruction pmtIns) {
pmtIns.setTransactionStatus(TransactionStatus.fail.getKey());
pmtIns.setUpdated(now);
pmtImdg.update(pmtIns);
return reqHelp.error(req.getId(), ClearingError.GeneralError);
}
private String extractPaymentPurpose(DmauRegistryRequest payload) {
if (!TextUtil.isEmpty(payload.getPaymentPurpose())) {
return payload.getPaymentPurpose();
}
if (CurrencyCode.RUB.equalsByKey(payload.getSecuritySymbol())) {
return localizer.resolve(ClearingMessage.withdrawalOfFundsDmau, LocaleUtil.ru, payload.getSecuritySymbol());
} else {
return localizer.resolve(ClearingMessage.withdrawalOfFundsDmau, LocaleUtil.eng, payload.getSecuritySymbol());
}
}
}

View file

@ -92,42 +92,55 @@ public class DmiService {
String securitySymbol, String securitySymbol,
String comment, String comment,
String contract) { String contract) {
findDmiByComment(tcrId, securitySymbol, comment).ifPresentOrElse(d__i -> { findDmiByComment(tcrId, securitySymbol, comment).ifPresentOrElse(
d__i.setContract(contract); (d__i) -> updateD__i(d__i, contract, securitySymbol, comment),
d__i.setUpdated(Instant.now()); () -> log.trace("didn't find DM*I by tcr.id={} securitySymbol={} comment={}",
try {
Long dbfId = Long.valueOf(contract);
SDf57 sdf57 = sDf57Imdg.getFirstObjectBySQL("dbfId=%d".formatted(dbfId));
if (sdf57 != null) {
ImdgPredicateBuilder pb = statementImdg.predicateBuilder();
ImdgPredicate stmtPrdct = pb.and(
pb.equals("inSDfId", sdf57.getId()),
pb.equals("inOutSDfType", InOutSDfType.type57.getKey())
);
Statement stmt = statementImdg.getFirstObjectByPredicate(stmtPrdct);
if (stmt != null && OperationStatus.Executed.equalsByKey(stmt.getOperationStatus())) {
d__i.setRegistryStatus(RegistryStatus.OK.getKey());
} else {
log.warn("tcrId={} securitySymbol={} comment={} sdf57 dbfId={} found, stmt.id={} status {}. d**i status will not be updated",
tcrId,
securitySymbol,
comment,
dbfId,
stmt != null ? stmt.getId() : null,
stmt != null ? stmt.getOperationStatus() : null);
}
}
} catch (Throwable e) {
log.error("sdf57 search by {}: error {}",
contract,
ExceptionUtils.getStackTrace(e));
}
rgsImdg.update(d__i);
log.trace("set {}.id={} contract {}", d__i.getRegistryCode(), d__i.getId(), contract);
}, () -> log.trace("didn't find DM*I by tcr.id={} securitySymbol={} comment={}",
tcrId, securitySymbol, comment)); tcrId, securitySymbol, comment));
} }
public void setContractByAcc(Long accId,
String securitySymbol,
String comment,
String contract) {
findDmiByCommentAcc(accId, securitySymbol, comment).ifPresentOrElse(
(d__i) -> updateD__i(d__i, contract, securitySymbol, comment),
() -> log.trace("didn't find DM*I by acc.id={} securitySymbol={} comment={}",
accId, securitySymbol, comment));
}
private void updateD__i(Registry d__i, String contract, String securitySymbol, String comment) {
d__i.setContract(contract);
d__i.setUpdated(Instant.now());
try {
Long dbfId = Long.valueOf(contract);
SDf57 sdf57 = sDf57Imdg.getFirstObjectBySQL("dbfId=%d".formatted(dbfId));
if (sdf57 != null) {
ImdgPredicateBuilder pb = statementImdg.predicateBuilder();
ImdgPredicate stmtPrdct = pb.and(
pb.equals("inSDfId", sdf57.getId()),
pb.equals("inOutSDfType", InOutSDfType.type57.getKey())
);
Statement stmt = statementImdg.getFirstObjectByPredicate(stmtPrdct);
if (stmt != null && OperationStatus.Executed.equalsByKey(stmt.getOperationStatus())) {
d__i.setRegistryStatus(RegistryStatus.OK.getKey());
} else {
log.warn("securitySymbol={} comment={} sdf57 dbfId={} found, stmt.id={} status {}. d**i status will not be updated",
securitySymbol,
comment,
dbfId,
stmt != null ? stmt.getId() : null,
stmt != null ? stmt.getOperationStatus() : null);
}
}
} catch (Throwable e) {
log.error("sdf57 search by {}: error {}",
contract,
ExceptionUtils.getStackTrace(e));
}
rgsImdg.update(d__i);
log.trace("set {}.id={} contract {}", d__i.getRegistryCode(), d__i.getId(), contract);
}
/** /**
* ищем D**I по tcrId/security/contract * ищем D**I по tcrId/security/contract
@ -178,6 +191,20 @@ public class DmiService {
return Optional.ofNullable(d__i); return Optional.ofNullable(d__i);
} }
private Optional<Registry> findDmiByCommentAcc(Long accId, String securitySymbol, String comment) {
ImdgPredicateBuilder pb = rgsImdg.predicateBuilder();
RegistryTradingParams rgsCde = RegistryTradingParams.D__I;
ImdgPredicate prdct = pb.and(
pb.sql(RegistryCodeSqlBuilder.getInstance(rgsCde).build()),
pb.equals("accountId", accId),
pb.equals("securitySymbol", securitySymbol),
pb.equals("comment", comment)
);
log.trace("searching D**I by {}", prdct);
Registry d__i = rgsImdg.getFirstObjectByPredicate(prdct);
return Optional.ofNullable(d__i);
}
public Optional<Registry> createDmi(Long tcrId, public Optional<Registry> createDmi(Long tcrId,
String securitySymbol, String securitySymbol,
BigDecimal summ) { BigDecimal summ) {

View file

@ -104,9 +104,10 @@ public enum Sdf55ValidationRule implements IValidationRule<ImdgValidationContext
} }
if (tcr == null) { if (tcr == null) {
return of(ClearingError.RecordNotFound, errMsg); log.debug("{}: TCR was not found; account will be used instead.", ruleName());
} else {
context.storeObject(ValidationStored.Sdf55Tcr, tcr);
} }
context.storeObject(ValidationStored.Sdf55Tcr, tcr);
return empty(); return empty();
} }
}, },

View file

@ -0,0 +1,37 @@
package ru.spcex.clearing.service.validation;
import java.math.BigDecimal;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.DmauRegistryRequest;
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.validation.IValidationRule;
public enum WithdrawalFundsValidationRule implements IValidationRule<ImdgValidationContext<DmauRegistryRequest>> {
Fields() {
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<DmauRegistryRequest> context) {
DmauRegistryRequest validatedObject = context.getValidatedObject();
if (validatedObject.getAmount() == null || validatedObject.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
return of(ClearingError.WrongField, "amount");
}
if (validatedObject.getBalance() == null || validatedObject.getBalance().compareTo(validatedObject.getAmount()) < 0) {
return of(ClearingError.WrongField, "balance");
}
if (validatedObject.getSenderId() == null) {
return of(ClearingError.WrongField, "senderId");
}
return empty();
}
},
;
private final static Logger log = LoggerFactory.getLogger(WithdrawalFundsValidationRule.class);
@Override
public String ruleName() {
return "WithdrawalFundsValidationRule." + name();
}
}

View file

@ -0,0 +1,9 @@
package ru.spcex.clearing.service.validation;
public enum WithdrawalFundsValidationStore {
CompanySymbol,
Company,
Account,
AssetTrio,
;
}

View file

@ -5,8 +5,8 @@ import ru.clearing.classes.statics.data.account.Account;
import ru.spcex.platform.enumeration.CurrencyCode; import ru.spcex.platform.enumeration.CurrencyCode;
public class LocaleUtil { public class LocaleUtil {
private static final Locale ru = new Locale("ru"); public static final Locale ru = new Locale("ru");
private static final Locale eng = new Locale("eng"); public static final Locale eng = new Locale("eng");
public static boolean isRus(Locale locale) { public static boolean isRus(Locale locale) {
return ru.equals(locale); return ru.equals(locale);

View file

@ -1,3 +1,4 @@
payment-instruction.purpose.withdrawal.tcr=Debt free cash withdrawal from TKR {0}. payment-instruction.purpose.withdrawal.tcr=Debt free cash withdrawal from TKR {0}.
payment-instruction.purpose.withdrawal=Debt free cash withdrawal. payment-instruction.purpose.withdrawal=Debt free cash withdrawal.
sdf54.urgent=urgent sdf54.urgent=urgent
withdrawal.funds.dmau=Withdrawal of funds from the register {0} of unidentified funds

View file

@ -1,3 +1,4 @@
payment-instruction.purpose.withdrawal.tcr=Возврат средств, свободных от обязательств с ТКР {0}. payment-instruction.purpose.withdrawal.tcr=Возврат средств, свободных от обязательств с ТКР {0}.
payment-instruction.purpose.withdrawal=Вывод средств. payment-instruction.purpose.withdrawal=Вывод средств.
sdf54.urgent=срочно sdf54.urgent=срочно
withdrawal.funds.dmau=Вывод средств с регистра невыясненных денежных средств по {0}

View file

@ -5343,7 +5343,9 @@ CREATE TABLE RISK_PARAMETER
CURRENCY_PAIR_ID BIGINT, CURRENCY_PAIR_ID BIGINT,
CREATED_AT timestamp, CREATED_AT timestamp,
UPDATED_AT timestamp, UPDATED_AT timestamp,
WORKFLOW_STATUS VARCHAR(4) WORKFLOW_STATUS VARCHAR(4),
PENALTY_VALUE numeric(72,18),
QUOTE_VALUE numeric(72,18)
); );
COMMENT ON TABLE RISK_PARAMETER IS 'Риск-параметры'; COMMENT ON TABLE RISK_PARAMETER IS 'Риск-параметры';
COMMENT ON COLUMN RISK_PARAMETER.ID IS 'Идентификатор записи'; COMMENT ON COLUMN RISK_PARAMETER.ID IS 'Идентификатор записи';
@ -5354,6 +5356,8 @@ COMMENT ON COLUMN RISK_PARAMETER.CURRENCY_PAIR_ID IS 'Идентификатор
COMMENT ON COLUMN RISK_PARAMETER.CREATED_AT IS 'Время создания записи'; COMMENT ON COLUMN RISK_PARAMETER.CREATED_AT IS 'Время создания записи';
COMMENT ON COLUMN RISK_PARAMETER.UPDATED_AT IS 'Время изменения записи'; COMMENT ON COLUMN RISK_PARAMETER.UPDATED_AT IS 'Время изменения записи';
COMMENT ON COLUMN RISK_PARAMETER.WORKFLOW_STATUS IS 'Статус'; COMMENT ON COLUMN RISK_PARAMETER.WORKFLOW_STATUS IS 'Статус';
COMMENT ON COLUMN RISK_PARAMETER.QUOTE_VALUE IS 'Ставка переноса сопряженной валюты';
COMMENT ON COLUMN RISK_PARAMETER.PENALTY_VALUE IS 'Ставка переноса при нехватке базовой валюты';
GRANT ALL PRIVILEGES ON TABLE RISK_PARAMETER TO clearing; GRANT ALL PRIVILEGES ON TABLE RISK_PARAMETER TO clearing;
-- rates - Расчетные курсы -- rates - Расчетные курсы
@ -5579,7 +5583,8 @@ CREATE TABLE ORDER_CURRENCY
COMMENT VARCHAR(255), COMMENT VARCHAR(255),
INITIAL_ORDER_ID BIGINT, INITIAL_ORDER_ID BIGINT,
CREATED_AT TIMESTAMP, CREATED_AT TIMESTAMP,
UPDATED_AT TIMESTAMP UPDATED_AT TIMESTAMP,
REGISTRY_ID BIGINT
); );
COMMENT ON TABLE ORDER_CURRENCY IS 'Заявки для заключения сделок переноса на валютном рынке'; COMMENT ON TABLE ORDER_CURRENCY IS 'Заявки для заключения сделок переноса на валютном рынке';
@ -5753,4 +5758,4 @@ GRANT ALL PRIVILEGES ON TABLE S_ORDERS TO clearing;
-- Data types -- Data types
INSERT INTO DB_VERSION(ID, VERSION) values (1, '3.22') ON CONFLICT (ID) DO UPDATE SET VERSION = EXCLUDED.VERSION INSERT INTO DB_VERSION(ID, VERSION) values (1, '3.23') ON CONFLICT (ID) DO UPDATE SET VERSION = EXCLUDED.VE RSION

View file

@ -7,5 +7,6 @@
/* Business objects */ /* Business objects */
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (1000, 'SCND', 'CURR', 'RUB', 'UVAK', 'Режим непрерывных торгов (торги иностранной валютой с Провайдером ликвидности)', 1, 'Continuous trading mode (foreign currency trading with a Liquidity Provider)') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (1000, 'SCND', 'CURR', 'RUB', 'UVAK', 'Режим непрерывных торгов (торги иностранной валютой с Провайдером ликвидности)', 1, 'Continuous trading mode (foreign currency trading with a Liquidity Provider)') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (1001, 'SCSP', 'CURR', 'RUB', 'TVAK', 'Технический режим переговорных сделок для сделок урегулирования', 1, 'Technical negotiated deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (1001, 'SCND', 'CURR', 'RUB', 'NVAK', 'Режим анонимных переговорных сделок (торги иностранной валютой с Провайдером ликвидности)', 1, 'Anonimous negotiated deals mode (foreign currency trading with a Liquidity Provider)') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (1002, 'SCSW', 'CURR', 'RUB', 'TVWK', 'Технический режим витринных переговорных сделок для сделок урегулирования', 1, 'Technical negotiated showcase deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2002, 'SCSP', 'CURR', 'RUB', 'TVAK', 'Технический режим переговорных сделок для сделок урегулирования', 1, 'Technical negotiated deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2003, 'SCSW', 'CURR', 'RUB', 'TVWK', 'Технический режим витринных переговорных сделок для сделок урегулирования', 1, 'Technical negotiated showcase deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;

View file

@ -7,5 +7,5 @@
/* Business objects */ /* Business objects */
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2000, 'SCND', 'CURR', 'RUB', 'UVAF', 'Режим непрерывных торгов (торги иностранной валютой с Провайдером ликвидности)', 1, 'Continuous trading mode (foreign currency trading with a Liquidity Provider)') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2000, 'SCND', 'CURR', 'RUB', 'UVAF', 'Режим непрерывных торгов (торги иностранной валютой с Провайдером ликвидности)', 1, 'Continuous trading mode (foreign currency trading with a Liquidity Provider)') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2001, 'SCSP', 'CURR', 'RUB', 'TVAF', 'Технический режим переговорных сделок для сделок урегулирования', 1, 'Technical negotiated deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2002, 'SCSP', 'CURR', 'RUB', 'TVAF', 'Технический режим переговорных сделок для сделок урегулирования', 1, 'Technical negotiated deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;
INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2002, 'SCSW', 'CURR', 'RUB', 'TVWF', 'Технический режим витринных переговорных сделок для сделок урегулирования', 1, 'Technical negotiated showcase deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION; INSERT INTO MARKET(ID, MARKET_TYPE, SECTION, SETTLEMENT_CURRENCY, CODE, NAME, EXCHANGE_ID, DESCRIPTION) values (2003, 'SCSW', 'CURR', 'RUB', 'TVWF', 'Технический режим витринных переговорных сделок для сделок урегулирования', 1, 'Technical negotiated showcase deals mode for settlement') ON CONFLICT (ID) DO UPDATE SET MARKET_TYPE = EXCLUDED.MARKET_TYPE, SECTION = EXCLUDED.SECTION, SETTLEMENT_CURRENCY = EXCLUDED.SETTLEMENT_CURRENCY, CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME, EXCHANGE_ID = EXCLUDED.EXCHANGE_ID, DESCRIPTION = EXCLUDED.DESCRIPTION;

View file

@ -3,6 +3,8 @@
ALTER TABLE RISK_PARAMETER ADD COLUMN IF NOT EXISTS QUOTE_VALUE NUMERIC(72, 18); ALTER TABLE RISK_PARAMETER ADD COLUMN IF NOT EXISTS QUOTE_VALUE NUMERIC(72, 18);
COMMENT ON COLUMN RISK_PARAMETER.QUOTE_VALUE IS 'Ставка переноса сопряженной валюты'; COMMENT ON COLUMN RISK_PARAMETER.QUOTE_VALUE IS 'Ставка переноса сопряженной валюты';
ALTER TABLE RISK_PARAMETER ADD COLUMN IF NOT EXISTS PENALTY_VALUE NUMERIC(72, 18);
COMMENT ON COLUMN RISK_PARAMETER.PENALTY_VALUE IS 'Ставка переноса при нехватке базовой валюты';
ALTER TABLE ORDER_CURRENCY ADD COLUMN IF NOT EXISTS REGISTRY_ID BIGINT; ALTER TABLE ORDER_CURRENCY ADD COLUMN IF NOT EXISTS REGISTRY_ID BIGINT;
COMMENT ON COLUMN ORDER_CURRENCY.REGISTRY_ID IS 'Идентификатор регистра'; COMMENT ON COLUMN ORDER_CURRENCY.REGISTRY_ID IS 'Идентификатор регистра';

View file

@ -0,0 +1,40 @@
package ru.spcex.clearing.dflt.management.component.validation;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;
import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.spcex.clearing.dflt.management.error.DefaultManagementErrors;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyUpdateRequest;
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.validation.IValidationRule;
public class OrderCurrencyCompanyNotChanged
implements IValidationRule<ImdgValidationContext<OrderCompanyUpdateRequest>> {
public static final OrderCurrencyCompanyNotChanged INSTANCE = new OrderCurrencyCompanyNotChanged();
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<OrderCompanyUpdateRequest> ctx) {
OrderCompanyUpdateRequest validatedObject = ctx.getValidatedObject();
OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
BigDecimal reqQuantityLog = Optional.ofNullable(validatedObject.getQuantityLot())
.map(BigDecimal::new)
.orElse(BigDecimal.ZERO);
BigDecimal entityQuantityLog = Optional.ofNullable(oc.getQuantityLot())
.orElse(BigDecimal.ZERO);
if (Objects.equals(validatedObject.getCompanyId(), oc.getCompanyId())
&& Objects.equals(validatedObject.getTradingClearingRegistryId(), oc.getTradingClearingRegistryId())
&& Objects.equals(reqQuantityLog, entityQuantityLog)) {
return of(DefaultManagementErrors.RecordAlreadyPresent);
}
return empty();
}
@Override
public String ruleName() {
return "OrderCurrencyCompanyNotChanged";
}
}

View file

@ -17,6 +17,7 @@ import ru.clearing.platform.dictionary.OrderStatusDictionary;
import ru.clearing.platform.dictionary.OvernightTypeDictionary; import ru.clearing.platform.dictionary.OvernightTypeDictionary;
import ru.clearing.platform.dictionary.SideDictionary; import ru.clearing.platform.dictionary.SideDictionary;
import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyByCompanyAndTcrAlreadyPresent; import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyByCompanyAndTcrAlreadyPresent;
import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyCompanyNotChanged;
import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyPLBLCheck; import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyPLBLCheck;
import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyValidationStored; import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyValidationStored;
import ru.spcex.clearing.dflt.management.component.validation.OvernightSettingAlreadyPresent; import ru.spcex.clearing.dflt.management.component.validation.OvernightSettingAlreadyPresent;
@ -355,7 +356,10 @@ public class ValidationConfig {
.builder(r -> { .builder(r -> {
OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency); OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
ImdgPredicateBuilder pb = tcrImdg.predicateBuilder(); ImdgPredicateBuilder pb = tcrImdg.predicateBuilder();
return pb.equals("companyId", oc.getCounterPartyId()); return pb.and(
pb.equals("companyId", oc.getCounterPartyId()),
pb.equals("code", oc.getCounterTradingCode())
);
}) })
.mapName(IMDGDistributedNames.Map_TradingClearingRegistry) .mapName(IMDGDistributedNames.Map_TradingClearingRegistry)
.errorProvider(r -> recordNotFound("counter (CK) TradingClearingRegistry")) .errorProvider(r -> recordNotFound("counter (CK) TradingClearingRegistry"))
@ -434,6 +438,7 @@ public class ValidationConfig {
companyPresentRule, companyPresentRule,
tcrPresent, tcrPresent,
QuantityLotGreaterThanZero.INSTANCE, QuantityLotGreaterThanZero.INSTANCE,
OrderCurrencyCompanyNotChanged.INSTANCE,
//OrderCurrencyByCompanyAndTcrAlreadyPresent.getInstance(OrderCompanyUpdateRequest.class), //OrderCurrencyByCompanyAndTcrAlreadyPresent.getInstance(OrderCompanyUpdateRequest.class),
OrderCurrencyPLBLCheck.INSTANCE, OrderCurrencyPLBLCheck.INSTANCE,
counterOrderPresent); counterOrderPresent);

View file

@ -38,7 +38,6 @@ import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCurrencyUpdate
import ru.spcex.clearing.platform.messaging.service.QueueConsumer; import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate;
import ru.spcex.clearing.platform.messaging.service.Status; import ru.spcex.clearing.platform.messaging.service.Status;
import ru.spcex.platform.enumeration.MarketType;
import ru.spcex.platform.enumeration.OrderStatus; import ru.spcex.platform.enumeration.OrderStatus;
import ru.spcex.platform.enumeration.OvernightType; import ru.spcex.platform.enumeration.OvernightType;
import ru.spcex.platform.enumeration.SettleCode; import ru.spcex.platform.enumeration.SettleCode;
@ -183,7 +182,7 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
if (OvernightType.SWAP.equalsByKey(order.getOvernightType())) { if (OvernightType.SWAP.equalsByKey(order.getOvernightType())) {
Pair<OrderCurrency, OrderCurrency> newSpotPair = swapToSpotSplit(order, rgs); Pair<OrderCurrency, OrderCurrency> newSpotPair = swapToSpotSplit(order, rgs);
log.info("deleting old OrderCurrency.id={}", order.getId()); log.info("deleting old OrderCurrency.id={}", order.getId());
orderCurrencyImdg.delete(order); reject(order);
orderCurrencyImdg.insert(newSpotPair.getFirst()); orderCurrencyImdg.insert(newSpotPair.getFirst());
orderCurrencyImdg.insert(newSpotPair.getSecond()); orderCurrencyImdg.insert(newSpotPair.getSecond());
log.info("created OrderCurrency[{}, {}]", log.info("created OrderCurrency[{}, {}]",
@ -192,14 +191,20 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
OrderCurrency pairOrder = validator.getStored(OrderCurrencyValidationStored.OrderCurrencySpotPair); OrderCurrency pairOrder = validator.getStored(OrderCurrencyValidationStored.OrderCurrencySpotPair);
OrderCurrency newSwapOrder = spotToSwap(order, pairOrder, rgs); OrderCurrency newSwapOrder = spotToSwap(order, pairOrder, rgs);
log.info("deleting old records OrderCurrency[{}, {}]", order.getId(), pairOrder.getId()); log.info("deleting old records OrderCurrency[{}, {}]", order.getId(), pairOrder.getId());
orderCurrencyImdg.delete(order); reject(order);
orderCurrencyImdg.delete(pairOrder); reject(pairOrder);
orderCurrencyImdg.insert(newSwapOrder); orderCurrencyImdg.insert(newSwapOrder);
log.info("created OrderCurrency.id={}", newSwapOrder.getId()); log.info("created OrderCurrency.id={}", newSwapOrder.getId());
} }
return null; return null;
} }
private void reject(OrderCurrency o) {
o.setStatus(OrderStatus.REJECTED.getKey());
o.setUpdated(Instant.now());
orderCurrencyImdg.update(o);
}
private Pair<OrderCurrency, OrderCurrency> swapToSpotSplit(OrderCurrency swap, Registry rgs) { private Pair<OrderCurrency, OrderCurrency> swapToSpotSplit(OrderCurrency swap, Registry rgs) {
OrderCurrency orderT0 = new OrderCurrency(); OrderCurrency orderT0 = new OrderCurrency();
OrderCurrency orderT1 = new OrderCurrency(); OrderCurrency orderT1 = new OrderCurrency();
@ -216,26 +221,34 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
} }
Market market = imdgQueryService.getSpotMarket(); Market market = imdgQueryService.getSpotMarket();
Listing listing = imdgQueryService.findListing(rgs, market.getCode()); Listing listingT0 = imdgQueryService.findListing(rgs, market.getCode(), SettleCode.T0.getKey());
Listing listingT1 = imdgQueryService.findListing(rgs, market.getCode(), SettleCode.T1.getKey());
//мб возвращать вместе с листингом? //мб возвращать вместе с листингом?
CurrencyPairDictionary currPair = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listing.getSecurityId()); CurrencyPairDictionary currPairT0 = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listingT0.getSecurityId());
SCrossRate rate = imdgQueryService.getSCrossRates(currPair.getBaseCurrency(), LocalDate.now()); SCrossRate rateT0 = imdgQueryService.getSCrossRates(currPairT0.getBaseCurrency(), LocalDate.now());
CurrencyPairDictionary currPairT1 = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listingT1.getSecurityId());
SCrossRate rateT1 = imdgQueryService.getSCrossRates(currPairT1.getBaseCurrency(), LocalDate.now());
var pair = new Pair<>(orderT0, orderT1); var pair = new Pair<>(orderT0, orderT1);
Pair.forEach(pair, o -> { Pair.forEach(pair, o -> {
copyCommonFields(swap, o); copyCommonFields(swap, o);
o.setSecurityId(listing.getSecurityId());
o.setSecuritySymbol(listing.getSymbolCode());
o.setListingId(listing.getId());
o.setOvernightType(OvernightType.SPOT.getKey()); o.setOvernightType(OvernightType.SPOT.getKey());
o.setMarket(MarketType.SCSP.getKey()); o.setMarket(market.getCode());
o.setCreated(Instant.now()); o.setCreated(Instant.now());
}); });
orderT0.setPrice(rate.getUnitRate()); updateOrderFieldsByListing(orderT0, listingT0);
orderT1.setPrice(BigDecimalUtil.safeSumBD(swap.getPrice(), rate.getUnitRate())); updateOrderFieldsByListing(orderT1, listingT1);
orderT0.setPrice(rateT0.getUnitRate());
orderT1.setPrice(BigDecimalUtil.safeSumBD(swap.getPrice(), rateT1.getUnitRate()));
return pair; return pair;
} }
private void updateOrderFieldsByListing(OrderCurrency order, Listing listing) {
order.setSecurityId(listing.getSecurityId());
order.setSecuritySymbol(listing.getSymbolCode());
order.setListingId(listing.getId());
}
private OrderCurrency spotToSwap(OrderCurrency order1, OrderCurrency order2, Registry rgs) { private OrderCurrency spotToSwap(OrderCurrency order1, OrderCurrency order2, Registry rgs) {
OrderCurrency spotT0; OrderCurrency spotT0;
OrderCurrency spotT1; OrderCurrency spotT1;
@ -257,14 +270,14 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
} }
Market market = imdgQueryService.getSwapMarket(); Market market = imdgQueryService.getSwapMarket();
Listing listing = imdgQueryService.findListing(rgs, market.getCode()); Listing listing = imdgQueryService.findListing(rgs, market.getCode(), SettleCode.T1.getKey());
copyCommonFields(order1, swap); copyCommonFields(order1, swap);
swap.setSecurityId(listing.getSecurityId()); swap.setSecurityId(listing.getSecurityId());
swap.setSecuritySymbol(listing.getSymbolCode()); swap.setSecuritySymbol(listing.getSymbolCode());
swap.setListingId(listing.getId()); swap.setListingId(listing.getId());
swap.setOvernightType(OvernightType.SWAP.getKey()); swap.setOvernightType(OvernightType.SWAP.getKey());
swap.setMarket(MarketType.SCSW.getKey()); swap.setMarket(market.getCode());
swap.setPrice(safeBD(spotT1.getPrice()).subtract(safeBD(spotT0.getPrice()))); swap.setPrice(safeBD(spotT1.getPrice()).subtract(safeBD(spotT0.getPrice())));
swap.setCreated(Instant.now()); swap.setCreated(Instant.now());
return swap; return swap;
@ -380,8 +393,8 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
OrderCurrency counterOrder = validator.getStored(OrderCurrencyValidationStored.CounterOrderCurrency); OrderCurrency counterOrder = validator.getStored(OrderCurrencyValidationStored.CounterOrderCurrency);
log.info("deleting OrderCurrency[{}, {}]", order.getId(), counterOrder.getId()); log.info("deleting OrderCurrency[{}, {}]", order.getId(), counterOrder.getId());
orderCurrencyImdg.delete(order); reject(order);
orderCurrencyImdg.delete(counterOrder); reject(counterOrder);
return null; return null;
} }
@ -406,6 +419,7 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
private void updateByFix(BaseRequest<OrderCurrencyStatusUpdateRequest> req) { private void updateByFix(BaseRequest<OrderCurrencyStatusUpdateRequest> req) {
OrderCurrencyStatusUpdateRequest payload = req.getRequestPayload(); OrderCurrencyStatusUpdateRequest payload = req.getRequestPayload();
for (Long orderId : payload.getIds()) { for (Long orderId : payload.getIds()) {
log.debug("Process updating status by orderNum {}", orderId);
SOrders sOrder = sOrdersImdg.getSingleObjectByPredicate( SOrders sOrder = sOrdersImdg.getSingleObjectByPredicate(
sOrdersImdg.predicateBuilder().equals("orderNum", orderId) sOrdersImdg.predicateBuilder().equals("orderNum", orderId)
); );
@ -416,7 +430,6 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
} }
orderCurrency.setStatus(status.getKey()); orderCurrency.setStatus(status.getKey());
orderCurrency.setUpdated(Instant.now()); orderCurrency.setUpdated(Instant.now());
// orderCurrency.setUpdated(sOrder.getTradeDate());//todo
orderCurrencyImdg.update(orderCurrency); orderCurrencyImdg.update(orderCurrency);
} }
} }

View file

@ -151,7 +151,7 @@ public class TaskListener extends QueueConsumer implements InitializingBean {
kafkaSender.sendRequestToQueue(Consts.NOTIFICATION_NEW, newReq); kafkaSender.sendRequestToQueue(Consts.NOTIFICATION_NEW, newReq);
} }
log.info("SDOR export and send command to fix, request={}", req); log.info("export to tri...");
try { try {
Collection<OrderCurrency> orders = imdgQueryService.getClccOrderCurrencyIds(); Collection<OrderCurrency> orders = imdgQueryService.getClccOrderCurrencyIds();
String content = orderCurrencyTriExportService.exportToTri(orders); String content = orderCurrencyTriExportService.exportToTri(orders);
@ -159,6 +159,7 @@ public class TaskListener extends QueueConsumer implements InitializingBean {
LocalDateTime.now().format(formatter))); LocalDateTime.now().format(formatter)));
Files.writeString(fileName, content, StandardCharsets.UTF_8); Files.writeString(fileName, content, StandardCharsets.UTF_8);
if (SendOrdersTypes.FIX == sendOrdersTypes) { if (SendOrdersTypes.FIX == sendOrdersTypes) {
log.info("send command to fix service...");
kafkaSender.sendRequestToQueue(Consts.SDOR_FIX_TASK, req.getRequestPayload()); kafkaSender.sendRequestToQueue(Consts.SDOR_FIX_TASK, req.getRequestPayload());
} }
} catch (IOException e) { } catch (IOException e) {

View file

@ -0,0 +1,11 @@
package ru.spcex.clearing.dflt.management.model;
import java.math.BigDecimal;
public record FinalCalculationResult(
BigDecimal price,
BigDecimal rate,
BigDecimal quantityLot,
CalculationResult calculationResult
) {
}

View file

@ -209,7 +209,6 @@ public class ImdgQueryService {
public BigDecimal defineTransferRate(Long currencyPairDictionaryId, String securitySymbol) { public BigDecimal defineTransferRate(Long currencyPairDictionaryId, String securitySymbol) {
ImdgPredicateBuilder pb = riskParameterImdg.predicateBuilder(); ImdgPredicateBuilder pb = riskParameterImdg.predicateBuilder();
boolean isRub = "RUB".equals(securitySymbol); boolean isRub = "RUB".equals(securitySymbol);
String baseCurrency = isRub ? "CNY" : securitySymbol;
RiskParameter riskParameter = riskParameterImdg.getSingleObjectByPredicate( RiskParameter riskParameter = riskParameterImdg.getSingleObjectByPredicate(
pb.and( pb.and(
@ -217,7 +216,7 @@ public class ImdgQueryService {
pb.equals("currencyPairId", currencyPairDictionaryId) pb.equals("currencyPairId", currencyPairDictionaryId)
) )
); );
return isRub ? riskParameter.getValue() : riskParameter.getQuoteValue(); return isRub ? riskParameter.getQuoteValue() : riskParameter.getPenaltyValue();
} }
public CurrencyPairDictionary getCurrencyPairDictionaryBySecurityId(Long securityId) { public CurrencyPairDictionary getCurrencyPairDictionaryBySecurityId(Long securityId) {

View file

@ -8,12 +8,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.misc.Listing; import ru.clearing.classes.statics.data.misc.Listing;
import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.clearing.classes.statics.data.misc.SCrossRate; import ru.clearing.classes.statics.data.misc.SCrossRate;
import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.security.Rates; import ru.clearing.classes.statics.data.security.Rates;
import ru.clearing.platform.dictionary.CurrencyPairDictionary; import ru.clearing.platform.dictionary.CurrencyPairDictionary;
import ru.spcex.clearing.dflt.management.model.CalculationResult; import ru.spcex.clearing.dflt.management.model.CalculationResult;
import ru.spcex.clearing.dflt.management.model.FinalCalculationResult;
import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext; import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext;
import ru.spcex.platform.enumeration.CurrencyCode; import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.SettleCode; import ru.spcex.platform.enumeration.SettleCode;
@ -32,10 +32,10 @@ public class OrderCalculationService {
this.transferDateService = transferDateService; this.transferDateService = transferDateService;
} }
public void calcAndEnrichSwap(OrderCurrency order, public FinalCalculationResult calcAndEnrichSwap(SettleCode settleCode,
OrderCurrencyContext context) { OrderCurrencyContext context) {
Registry registry = context.getRegistry(); Registry registry = context.getRegistry();
Listing listing = imdgQueryService.findListing(registry, context.getMarket().getCode(), order.getSettleCode()); Listing listing = imdgQueryService.findListing(registry, context.getMarket().getCode(), settleCode.getKey());
CurrencyPairDictionary pair = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listing.getSecurityId()); CurrencyPairDictionary pair = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listing.getSecurityId());
BigDecimal s = imdgQueryService.defineTransferRate(pair.getId(), registry.getSecuritySymbol()); BigDecimal s = imdgQueryService.defineTransferRate(pair.getId(), registry.getSecuritySymbol());
@ -46,16 +46,15 @@ public class OrderCalculationService {
: calculationSums(registry, listing.getLotSize().intValue(), s, n); : calculationSums(registry, listing.getLotSize().intValue(), s, n);
BigDecimal quantityLot = result.sum3().abs(); BigDecimal quantityLot = result.sum3().abs();
BigDecimal price = swapPriceCalc(pair.getBaseCurrency(), s, n); BigDecimal price = swapPriceCalc(pair.getBaseCurrency(), s, n, listing.getPrecision().intValue());
order.setPrice(price); return new FinalCalculationResult(price, s, quantityLot, result);
order.setQuantityLot(quantityLot);
} }
public void calcAndEnrichSpot(OrderCurrency order, public FinalCalculationResult calcAndEnrichSpot(SettleCode settleCode,
OrderCurrencyContext context) { OrderCurrencyContext context) {
Registry registry = context.getRegistry(); Registry registry = context.getRegistry();
Listing listing = imdgQueryService.findListing(registry, context.getMarket().getCode(), order.getSettleCode()); Listing listing = imdgQueryService.findListing(registry, context.getMarket().getCode(), settleCode.getKey());
CurrencyPairDictionary pair = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listing.getSecurityId()); CurrencyPairDictionary pair = imdgQueryService.getCurrencyPairDictionaryBySecurityId(listing.getSecurityId());
BigDecimal s = imdgQueryService.defineTransferRate(pair.getId(), registry.getSecuritySymbol()); BigDecimal s = imdgQueryService.defineTransferRate(pair.getId(), registry.getSecuritySymbol());
@ -68,32 +67,28 @@ public class OrderCalculationService {
SCrossRate rates = imdgQueryService.getSCrossRates(pair.getBaseCurrency(), LocalDate.now()); SCrossRate rates = imdgQueryService.getSCrossRates(pair.getBaseCurrency(), LocalDate.now());
BigDecimal quantityLot; BigDecimal quantityLot;
BigDecimal price; BigDecimal price;
if (SettleCode.T0.equalsByKey(order.getSettleCode())) { if (SettleCode.T0 == settleCode) {
quantityLot = result.sum3().abs(); quantityLot = result.sum3().abs();
price = rates.getUnitRate(); price = rates.getUnitRate().setScale(listing.getPrecision().intValue(), RoundingMode.HALF_UP);
} else { } else {
quantityLot = result.sum5().abs(); quantityLot = result.sum5().abs();
BigDecimal delta = swapPriceCalc(pair.getBaseCurrency(), s, n); BigDecimal delta = swapPriceCalc(pair.getBaseCurrency(), s, n, listing.getPrecision().intValue());
price = rates.getUnitRate().add(delta); price = rates.getUnitRate().add(delta).setScale(listing.getPrecision().intValue(), RoundingMode.HALF_UP);;
} }
order.setPrice(price); return new FinalCalculationResult(price, s, quantityLot, result);
order.setQuantityLot(quantityLot);
order.setRate(s);
} }
private CalculationResult calculationSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { private CalculationResult calculationSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) {
BigDecimal planPosition = registry.getBalance(); BigDecimal planPosition = registry.getBalance();
BigDecimal sum1 = planPosition.abs(); BigDecimal sum1 = planPosition.abs();
Registry rubRegistry = imdgQueryService.findAmtRegistry(registry, "RUB"); SCrossRate sCrossRates = imdgQueryService.getSCrossRates(registry.getSecuritySymbol(), LocalDate.now());
BigDecimal planPositionRub = rubRegistry.getBalance(); if (sCrossRates == null) {
throw new IllegalStateException(String.format("Not fond rate by security symbol: %s", CurrencyCode.CNY.getKey()));
Rates rates = imdgQueryService.getRate(registry.getSecuritySymbol(), LocalDate.now()); }
BigDecimal rateValue = rates.getValue(); BigDecimal rateValue = sCrossRates.getUnitRate();
BigDecimal sum3 = mathCeil(sum1, lotSize);
BigDecimal sum2 = planPositionRub.divide(rateValue, lotSize, RoundingMode.HALF_DOWN);
BigDecimal sum3 = sum1.setScale(lotSize, RoundingMode.CEILING);
BigDecimal sum4 = sum3.multiply(rateValue, MC).negate(); BigDecimal sum4 = sum3.multiply(rateValue, MC).negate();
BigDecimal sum5 = sum3.negate(); BigDecimal sum5 = sum3.negate();
@ -104,7 +99,7 @@ public class OrderCalculationService {
BigDecimal sum6 = sum3.multiply(rateValue, MC).multiply(multiplier, MC); BigDecimal sum6 = sum3.multiply(rateValue, MC).multiply(multiplier, MC);
return new CalculationResult(sum1, sum2, sum3, sum4, sum5, sum6); return new CalculationResult(sum1, BigDecimal.ZERO, sum3, sum4, sum5, sum6);
} }
private CalculationResult calculationRubSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) { private CalculationResult calculationRubSums(Registry registry, Integer lotSize, BigDecimal s, BigDecimal n) {
@ -115,13 +110,14 @@ public class OrderCalculationService {
} }
BigDecimal rateValue = sCrossRates.getUnitRate(); BigDecimal rateValue = sCrossRates.getUnitRate();
BigDecimal sum1 = planPosition.divide(rateValue, MC); BigDecimal sum1 = planPosition.divide(rateValue, MC).abs();
Registry cnyRegistry = imdgQueryService.findAmtRegistry(registry, "CNY"); Registry amtRegistry = imdgQueryService.findAmtRegistry(registry, "CNY");
BigDecimal planPositionCny = cnyRegistry.getBalance(); BigDecimal planPositionCny = amtRegistry.getBalance();
BigDecimal sum2 = planPositionCny.divide(rateValue, lotSize, RoundingMode.HALF_DOWN); BigDecimal sum2 = planPositionCny.setScale(lotSize, RoundingMode.HALF_DOWN);
BigDecimal sum3 = sum1.setScale(lotSize, RoundingMode.CEILING).min(sum2).negate(); BigDecimal y = sum1.min(sum2);
BigDecimal sum3 = mathCeil(y, lotSize).negate();
BigDecimal sum4 = sum3.multiply(rateValue, MC).negate(); BigDecimal sum4 = sum3.multiply(rateValue, MC).negate();
BigDecimal sum5 = sum3.negate(); BigDecimal sum5 = sum3.negate();
@ -135,7 +131,7 @@ public class OrderCalculationService {
return new CalculationResult(sum1, sum2, sum3, sum4, sum5, sum6); return new CalculationResult(sum1, sum2, sum3, sum4, sum5, sum6);
} }
private BigDecimal swapPriceCalc(String baseCurrency, BigDecimal s, BigDecimal n) { private BigDecimal swapPriceCalc(String baseCurrency, BigDecimal s, BigDecimal n, int precision) {
Rates rate = imdgQueryService.getRate(baseCurrency, LocalDate.now()); Rates rate = imdgQueryService.getRate(baseCurrency, LocalDate.now());
BigDecimal p = BigDecimal.ONE.divide(n, MC); BigDecimal p = BigDecimal.ONE.divide(n, MC);
log.info("Calculation swap price: {}*{}*{}", s, n, p); log.info("Calculation swap price: {}*{}*{}", s, n, p);
@ -143,10 +139,14 @@ public class OrderCalculationService {
.multiply(s, MC) .multiply(s, MC)
.multiply(n, MC) .multiply(n, MC)
.multiply(p, MC) .multiply(p, MC)
.setScale(8, RoundingMode.HALF_UP); .setScale(precision, RoundingMode.HALF_UP);
} }
private boolean isRub(Registry registry) { private boolean isRub(Registry registry) {
return "RUB".equals(registry.getSecuritySymbol()); return "RUB".equals(registry.getSecuritySymbol());
} }
private BigDecimal mathCeil(BigDecimal val, Integer multiplicity) {
return BigDecimal.valueOf(Math.ceil(val.doubleValue() / multiplicity));
}
} }

View file

@ -7,6 +7,7 @@ import ru.clearing.classes.statics.data.misc.Market;
import ru.clearing.classes.statics.data.misc.OrderCurrency; import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.registry.Registry;
import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; import ru.spcex.clearing.dflt.management.model.CounterpartyInfo;
import ru.spcex.clearing.dflt.management.model.FinalCalculationResult;
import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext; import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext;
import ru.spcex.platform.enumeration.SettleCode; import ru.spcex.platform.enumeration.SettleCode;
import static ru.spcex.platform.enumeration.SettleCode.T0; import static ru.spcex.platform.enumeration.SettleCode.T0;
@ -20,7 +21,9 @@ public class OrderTemplateBuilder {
this.imdgQueryService = imdgQueryService; this.imdgQueryService = imdgQueryService;
} }
public OrderCurrency buildPrimaryTransfer(OrderCurrencyContext context, SettleCode settleCode) { public OrderCurrency buildPrimaryTransfer(OrderCurrencyContext context,
SettleCode settleCode,
FinalCalculationResult finalCalculationResult) {
OrderCurrency orderCurrency; OrderCurrency orderCurrency;
switch (context.getOrderCategory()) { switch (context.getOrderCategory()) {
case "PL" -> orderCurrency = buildPrimaryTransferPl(context); case "PL" -> orderCurrency = buildPrimaryTransferPl(context);
@ -28,11 +31,13 @@ public class OrderTemplateBuilder {
default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory()); default -> throw new IllegalArgumentException("Unsupported order category: " + context.getOrderCategory());
} }
Side side = resolvePrimaryTransferSide(context.getRegistry(), settleCode); Side side = resolvePrimaryTransferSide(context.getRegistry(), settleCode);
enrichOrder(orderCurrency, context, side, "overnight", settleCode); enrichOrder(orderCurrency, context, side, "overnight", settleCode, finalCalculationResult);
return orderCurrency; return orderCurrency;
} }
public OrderCurrency buildCounterTransfer(OrderCurrencyContext context, SettleCode settleCode) { public OrderCurrency buildCounterTransfer(OrderCurrencyContext context,
SettleCode settleCode,
FinalCalculationResult finalCalculationResult) {
OrderCurrency orderCurrency; OrderCurrency orderCurrency;
switch (context.getOrderCategory()) { switch (context.getOrderCategory()) {
case "PL" -> orderCurrency = buildCounterTransferPl(context); case "PL" -> orderCurrency = buildCounterTransferPl(context);
@ -41,11 +46,13 @@ public class OrderTemplateBuilder {
} }
; ;
Side side = resolveCounterTransferSide(context.getRegistry(), settleCode); Side side = resolveCounterTransferSide(context.getRegistry(), settleCode);
enrichOrder(orderCurrency, context, side, "overnight", settleCode); enrichOrder(orderCurrency, context, side, "overnight", settleCode, finalCalculationResult);
return orderCurrency; return orderCurrency;
} }
public OrderCurrency buildPrimaryBalancing(OrderCurrencyContext context, SettleCode settleCode) { public OrderCurrency buildPrimaryBalancing(OrderCurrencyContext context,
SettleCode settleCode,
FinalCalculationResult finalCalculationResult) {
OrderCurrency orderCurrency; OrderCurrency orderCurrency;
switch (context.getOrderCategory()) { switch (context.getOrderCategory()) {
case "PL" -> orderCurrency = buildPrimaryBalancingPl(context); case "PL" -> orderCurrency = buildPrimaryBalancingPl(context);
@ -54,11 +61,13 @@ public class OrderTemplateBuilder {
} }
; ;
Side side = resolvePrimaryBalancingSide(context.getRegistry(), settleCode); Side side = resolvePrimaryBalancingSide(context.getRegistry(), settleCode);
enrichOrder(orderCurrency, context, side, "balance", settleCode); enrichOrder(orderCurrency, context, side, "balance", settleCode, finalCalculationResult);
return orderCurrency; return orderCurrency;
} }
public OrderCurrency buildCounterBalancing(OrderCurrencyContext context, SettleCode settleCode) { public OrderCurrency buildCounterBalancing(OrderCurrencyContext context,
SettleCode settleCode,
FinalCalculationResult finalCalculationResult) {
OrderCurrency orderCurrency; OrderCurrency orderCurrency;
switch (context.getOrderCategory()) { switch (context.getOrderCategory()) {
case "PL" -> orderCurrency = buildCounterBalancingPl(context); case "PL" -> orderCurrency = buildCounterBalancingPl(context);
@ -67,7 +76,7 @@ public class OrderTemplateBuilder {
} }
; ;
Side side = resolveCounterBalancingSide(context.getRegistry(), settleCode); Side side = resolveCounterBalancingSide(context.getRegistry(), settleCode);
enrichOrder(orderCurrency, context, side, "balance", settleCode); enrichOrder(orderCurrency, context, side, "balance", settleCode, finalCalculationResult);
return orderCurrency; return orderCurrency;
} }
@ -195,7 +204,8 @@ public class OrderTemplateBuilder {
OrderCurrencyContext context, OrderCurrencyContext context,
Side side, Side side,
String comment, String comment,
SettleCode settleCode) { SettleCode settleCode,
FinalCalculationResult finalCalculationResult) {
Registry registry = context.getRegistry(); Registry registry = context.getRegistry();
Market market = context.getMarket(); Market market = context.getMarket();
Listing listing = imdgQueryService.findListing(registry, market.getCode(), settleCode.getKey()); Listing listing = imdgQueryService.findListing(registry, market.getCode(), settleCode.getKey());
@ -212,28 +222,32 @@ public class OrderTemplateBuilder {
order.setComment(comment); order.setComment(comment);
order.setRegistryId(registry.getId()); order.setRegistryId(registry.getId());
order.setCreated(Instant.now()); order.setCreated(Instant.now());
order.setPrice(finalCalculationResult.price());
order.setQuantityLot(finalCalculationResult.quantityLot());
order.setRate(finalCalculationResult.rate());
} }
private Side resolvePrimaryTransferSide(Registry registry, SettleCode settleCode) { public Side resolvePrimaryTransferSide(Registry registry, SettleCode settleCode) {
Side side = T0 == settleCode ? Side.BUY : Side.SELL;
return isRub(registry) ? side : side.revers();
}
private Side resolveCounterTransferSide(Registry registry, SettleCode settleCode) {
Side side = T0 == settleCode ? Side.SELL : Side.BUY; Side side = T0 == settleCode ? Side.SELL : Side.BUY;
return isRub(registry) ? side : side.revers(); return isRub(registry) ? side : side.revers();
} }
private Side resolvePrimaryBalancingSide(Registry registry, SettleCode settleCode) { public Side resolveCounterTransferSide(Registry registry, SettleCode settleCode) {
Side side = T0 == settleCode ? Side.SELL : Side.BUY; Side side = T0 == settleCode ? Side.BUY : Side.SELL;
return isRub(registry) ? side : side.revers(); return isRub(registry) ? side : side.revers();
} }
private Side resolveCounterBalancingSide(Registry registry, SettleCode settleCode) { public Side resolvePrimaryBalancingSide(Registry registry, SettleCode settleCode) {
Side side = T0 == settleCode ? Side.BUY : Side.SELL; Side side = T0 == settleCode ? Side.BUY : Side.SELL;
return isRub(registry) ? side : side.revers(); return isRub(registry) ? side : side.revers();
} }
public Side resolveCounterBalancingSide(Registry registry, SettleCode settleCode) {
Side side = T0 == settleCode ? Side.SELL : Side.BUY;
return isRub(registry) ? side : side.revers();
}
private boolean isRub(Registry registry) { private boolean isRub(Registry registry) {
return "RUB".equals(registry.getSecuritySymbol()); return "RUB".equals(registry.getSecuritySymbol());
} }

View file

@ -1,5 +1,6 @@
package ru.spcex.clearing.dflt.management.service; package ru.spcex.clearing.dflt.management.service;
import java.math.BigDecimal;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -8,14 +9,17 @@ import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.security.RiskParameter; import ru.clearing.classes.statics.data.security.RiskParameter;
import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; import ru.spcex.clearing.dflt.management.model.CounterpartyInfo;
import ru.spcex.clearing.dflt.management.model.FinalCalculationResult;
import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext; import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext;
import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.enumeration.CompanyRole; import ru.spcex.platform.enumeration.CompanyRole;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.OvernightType; import ru.spcex.platform.enumeration.OvernightType;
import ru.spcex.platform.enumeration.RegistryStatus; import ru.spcex.platform.enumeration.RegistryStatus;
import ru.spcex.platform.enumeration.SettleCode; import ru.spcex.platform.enumeration.SettleCode;
import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.utils.enumeration.IEnumKey;
@Service @Service
public class SpotOvernightOrderService { public class SpotOvernightOrderService {
@ -46,6 +50,7 @@ public class SpotOvernightOrderService {
CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P"); CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P");
CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC); CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC);
CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM); CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM);
CurrencyCode currency = IEnumKey.getEnumByKey(CurrencyCode.class, registry.getSecuritySymbol());
OrderCurrencyContext context = OrderCurrencyContext.builder() OrderCurrencyContext context = OrderCurrencyContext.builder()
.registry(registry) .registry(registry)
@ -58,25 +63,23 @@ public class SpotOvernightOrderService {
.overnightType(OvernightType.SPOT) .overnightType(OvernightType.SPOT)
.build(); .build();
OrderCurrency primaryTransferT0 = templateBuilder.buildPrimaryTransfer(context, SettleCode.T0); FinalCalculationResult finalCalculationResultT0 = swapCalculationService.calcAndEnrichSpot(SettleCode.T0, context);
OrderCurrency counterTransferT0 = templateBuilder.buildCounterTransfer(context, SettleCode.T0); FinalCalculationResult finalCalculationResultT1 = swapCalculationService.calcAndEnrichSpot(SettleCode.T1, context);
OrderCurrency primaryTransferT1 = templateBuilder.buildPrimaryTransfer(context, SettleCode.T1);
OrderCurrency counterTransferT1 = templateBuilder.buildCounterTransfer(context, SettleCode.T1);
OrderCurrency primaryBalancingT0 = templateBuilder.buildPrimaryBalancing(context, SettleCode.T0); if (CurrencyCode.RUB == currency &&
OrderCurrency counterBalancingT0 = templateBuilder.buildCounterBalancing(context, SettleCode.T0); finalCalculationResultT0.calculationResult().sum3().compareTo(BigDecimal.ZERO) >= 0) {
OrderCurrency primaryBalancingT1 = templateBuilder.buildPrimaryBalancing(context, SettleCode.T1); log.warn("QuantityLot greater than zero; skip forming order currency");
OrderCurrency counterBalancingT1 = templateBuilder.buildCounterBalancing(context, SettleCode.T1); return;
}
OrderCurrency primaryTransferT0 = templateBuilder.buildPrimaryTransfer(context, SettleCode.T0, finalCalculationResultT0);
OrderCurrency counterTransferT0 = templateBuilder.buildCounterTransfer(context, SettleCode.T0, finalCalculationResultT0);
OrderCurrency primaryTransferT1 = templateBuilder.buildPrimaryTransfer(context, SettleCode.T1, finalCalculationResultT1);
OrderCurrency counterTransferT1 = templateBuilder.buildCounterTransfer(context, SettleCode.T1, finalCalculationResultT1);
swapCalculationService.calcAndEnrichSpot(primaryTransferT0, context); OrderCurrency primaryBalancingT0 = templateBuilder.buildPrimaryBalancing(context, SettleCode.T0, finalCalculationResultT0);
swapCalculationService.calcAndEnrichSpot(counterTransferT0, context); OrderCurrency counterBalancingT0 = templateBuilder.buildCounterBalancing(context, SettleCode.T0, finalCalculationResultT0);
swapCalculationService.calcAndEnrichSpot(primaryTransferT1, context); OrderCurrency primaryBalancingT1 = templateBuilder.buildPrimaryBalancing(context, SettleCode.T1, finalCalculationResultT1);
swapCalculationService.calcAndEnrichSpot(counterTransferT1, context); OrderCurrency counterBalancingT1 = templateBuilder.buildCounterBalancing(context, SettleCode.T1, finalCalculationResultT1);
swapCalculationService.calcAndEnrichSpot(primaryBalancingT0, context);
swapCalculationService.calcAndEnrichSpot(counterBalancingT0, context);
swapCalculationService.calcAndEnrichSpot(primaryBalancingT1, context);
swapCalculationService.calcAndEnrichSpot(counterBalancingT1, context);
orderCurrencyImdg.insert(primaryTransferT0); orderCurrencyImdg.insert(primaryTransferT0);
orderCurrencyImdg.insert(counterTransferT0); orderCurrencyImdg.insert(counterTransferT0);

View file

@ -1,5 +1,6 @@
package ru.spcex.clearing.dflt.management.service; package ru.spcex.clearing.dflt.management.service;
import java.math.BigDecimal;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -8,14 +9,17 @@ import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.clearing.classes.statics.data.registry.Registry; import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.security.RiskParameter; import ru.clearing.classes.statics.data.security.RiskParameter;
import ru.spcex.clearing.dflt.management.model.CounterpartyInfo; import ru.spcex.clearing.dflt.management.model.CounterpartyInfo;
import ru.spcex.clearing.dflt.management.model.FinalCalculationResult;
import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext; import ru.spcex.clearing.dflt.management.model.OrderCurrencyContext;
import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.enumeration.CompanyRole; import ru.spcex.platform.enumeration.CompanyRole;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.enumeration.OvernightType; import ru.spcex.platform.enumeration.OvernightType;
import ru.spcex.platform.enumeration.RegistryStatus; import ru.spcex.platform.enumeration.RegistryStatus;
import ru.spcex.platform.enumeration.SettleCode; import ru.spcex.platform.enumeration.SettleCode;
import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.utils.enumeration.IEnumKey;
@Service @Service
public class SwapOvernightOrderService { public class SwapOvernightOrderService {
@ -46,7 +50,7 @@ public class SwapOvernightOrderService {
CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P"); CounterpartyInfo categoryP = imdgQueryService.getCompanyByClearingCategory("P");
CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC); CounterpartyInfo clcc = imdgQueryService.getCompanyByRole(CompanyRole.CLCC);
CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM); CounterpartyInfo rgcm = imdgQueryService.getCompanyByRole(CompanyRole.RGCM);
CurrencyCode currency = IEnumKey.getEnumByKey(CurrencyCode.class, registry.getSecuritySymbol());
OrderCurrencyContext context = OrderCurrencyContext.builder() OrderCurrencyContext context = OrderCurrencyContext.builder()
.registry(registry) .registry(registry)
.orderCategory(orderCategory) .orderCategory(orderCategory)
@ -58,15 +62,17 @@ public class SwapOvernightOrderService {
.overnightType(OvernightType.SWAP) .overnightType(OvernightType.SWAP)
.build(); .build();
OrderCurrency primaryTransfer = templateBuilder.buildPrimaryTransfer(context, SettleCode.T1); FinalCalculationResult finalCalculationResult = swapCalculationService.calcAndEnrichSwap(SettleCode.T1, context);
OrderCurrency counterTransfer = templateBuilder.buildCounterTransfer(context, SettleCode.T1); if (CurrencyCode.RUB == currency &&
OrderCurrency primaryBalancing = templateBuilder.buildPrimaryBalancing(context, SettleCode.T1); finalCalculationResult.calculationResult().sum3().compareTo(BigDecimal.ZERO) >= 0) {
OrderCurrency counterBalancing = templateBuilder.buildCounterBalancing(context, SettleCode.T1); log.warn("QuantityLot greater than zero; skip forming order currency");
return;
}
swapCalculationService.calcAndEnrichSwap(primaryTransfer, context); OrderCurrency primaryTransfer = templateBuilder.buildPrimaryTransfer(context, SettleCode.T1, finalCalculationResult);
swapCalculationService.calcAndEnrichSwap(counterTransfer, context); OrderCurrency counterTransfer = templateBuilder.buildCounterTransfer(context, SettleCode.T1, finalCalculationResult);
swapCalculationService.calcAndEnrichSwap(primaryBalancing, context); OrderCurrency primaryBalancing = templateBuilder.buildPrimaryBalancing(context, SettleCode.T1, finalCalculationResult);
swapCalculationService.calcAndEnrichSwap(counterBalancing, context); OrderCurrency counterBalancing = templateBuilder.buildCounterBalancing(context, SettleCode.T1, finalCalculationResult);
orderCurrencyImdg.insert(primaryTransfer); orderCurrencyImdg.insert(primaryTransfer);
orderCurrencyImdg.insert(counterTransfer); orderCurrencyImdg.insert(counterTransfer);

View file

@ -0,0 +1,32 @@
package ru.spcex.clearing.dflt.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.spcex.clearing.dflt.management.service.OrderTemplateBuilder;
import ru.spcex.platform.enumeration.SettleCode;
import ru.spcex.platform.enumeration.Side;
public class OrderTemplateBuilderTest {
@Test
public void testSwapNotRubT1() {
OrderTemplateBuilder builder = new OrderTemplateBuilder(null);
Assertions.assertEquals(Side.SELL, builder.resolvePrimaryTransferSide(createNotRubRegistry(), SettleCode.T1));
Assertions.assertEquals(Side.BUY, builder.resolvePrimaryTransferSide(createRubRegistry(), SettleCode.T1));
Assertions.assertEquals(Side.BUY, builder.resolvePrimaryTransferSide(createNotRubRegistry(), SettleCode.T0));
Assertions.assertEquals(Side.SELL, builder.resolvePrimaryTransferSide(createNotRubRegistry(), SettleCode.T1));
}
public static Registry createNotRubRegistry() {
Registry registry = new Registry();
registry.setSecuritySymbol("CNY");
return registry;
}
public static Registry createRubRegistry() {
Registry registry = new Registry();
registry.setSecuritySymbol("RUB");
return registry;
}
}

View file

@ -1,13 +1,47 @@
package ru.spcex.clearing.fix.quickfix; package ru.spcex.clearing.fix.quickfix;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.regex.Pattern;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import quickfix.*; import quickfix.ConfigError;
import quickfix.field.*; import quickfix.DoNotSend;
import quickfix.FieldNotFound;
import quickfix.IncorrectDataFormat;
import quickfix.IncorrectTagValue;
import quickfix.LogFactory;
import quickfix.Message;
import quickfix.MessageFactory;
import quickfix.MessageStoreFactory;
import quickfix.RejectLogon;
import quickfix.Session;
import quickfix.SessionID;
import quickfix.SessionNotFound;
import quickfix.SessionSettings;
import quickfix.SocketInitiator;
import quickfix.StringField;
import quickfix.UnsupportedMessageType;
import quickfix.field.Account;
import quickfix.field.ClOrdID;
import quickfix.field.ContraBroker;
import quickfix.field.ExDestination;
import quickfix.field.HandlInst;
import quickfix.field.OrdStatus;
import quickfix.field.OrdType;
import quickfix.field.OrderQty;
import quickfix.field.Price;
import quickfix.field.SecurityID;
import quickfix.field.SecurityIDSource;
import quickfix.field.SettlType;
import quickfix.field.Side;
import quickfix.field.Text;
import quickfix.field.TransactTime;
import quickfix.fix44.ExecutionReport; import quickfix.fix44.ExecutionReport;
import quickfix.fix44.MessageCracker; import quickfix.fix44.MessageCracker;
import quickfix.fix44.NewOrderSingle; import quickfix.fix44.NewOrderSingle;
@ -21,11 +55,6 @@ import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.ImdgTransaction; import ru.spcex.platform.imdg.api.ImdgTransaction;
import java.math.BigDecimal;
import java.time.ZoneOffset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Service @Service
public class Application extends MessageCracker implements quickfix.Application, InitializingBean, DisposableBean { public class Application extends MessageCracker implements quickfix.Application, InitializingBean, DisposableBean {
private static final Logger log = LoggerFactory.getLogger(Application.class); private static final Logger log = LoggerFactory.getLogger(Application.class);
@ -113,25 +142,19 @@ public class Application extends MessageCracker implements quickfix.Application,
ImdgTransaction transaction = imdgProvider.newTransaction(); ImdgTransaction transaction = imdgProvider.newTransaction();
transaction.beginTransaction(); transaction.beginTransaction();
try { try {
if (executionReport.isSetField(OrderID.FIELD) && executionReport.isSetField(ExDestination.FIELD) && executionReport.isSetField(TransactTime.FIELD)) { if (executionReport.isSetField(ClOrdID.FIELD) && executionReport.isSetField(ExDestination.FIELD) && executionReport.isSetField(TransactTime.FIELD) && executionReport.isSetField(OrdStatus.FIELD)) {
OrderID orderId = executionReport.getOrderID(); // OrderID orderId = executionReport.getOrderID();
ExDestination exDestination = (ExDestination) executionReport.getField(new ExDestination()); ExDestination exDestination = (ExDestination) executionReport.getField(new ExDestination());
TransactTime transactTime = executionReport.getTransactTime(); TransactTime transactTime = executionReport.getTransactTime();
SecurityID securityID = executionReport.getSecurityID();
Price price = executionReport.getPrice();
OrderQty orderQty = executionReport.getOrderQty();
Account account = executionReport.getAccount();
Side side = executionReport.getSide();
Currency currency = executionReport.getCurrency();
SettlType settlType = executionReport.getSettlType();
OrdStatus ordStatus = executionReport.getOrdStatus(); OrdStatus ordStatus = executionReport.getOrdStatus();
ClOrdID clOrdID = executionReport.getClOrdID();
Pattern orderIdPattern = Pattern.compile("(\\d{6})-([A-Z]+)-(\\d+)"); Pattern orderIdPattern = Pattern.compile("(\\d{6})-([A-Z]+)-(\\d+)");
Matcher matcher = orderIdPattern.matcher(orderId.getValue()); // Matcher matcher = orderIdPattern.matcher(orderId.getValue());
if (matcher.matches()) { // if (matcher.matches()) {
// LocalDate tradeDate = LocalDate.parse(matcher.group(1), DateTimeFormatter.ofPattern("yyMMdd")); // LocalDate tradeDate = LocalDate.parse(matcher.group(1), DateTimeFormatter.ofPattern("yyMMdd"));
// String classCode = matcher.group(2); // String classCode = matcher.group(2);
Long orderNum = Long.parseLong(matcher.group(3)); Long orderNum = Long.parseLong(clOrdID.getValue());
Imdg<SOrders> sOrdersImdg = transaction.getImdg(IMDGDistributedNames.Map_SOrders, SOrders.class); Imdg<SOrders> sOrdersImdg = transaction.getImdg(IMDGDistributedNames.Map_SOrders, SOrders.class);
SOrders sOrders = sOrdersImdg.getSingleObjectByPredicate(sOrdersImdg.predicateBuilder().and( SOrders sOrders = sOrdersImdg.getSingleObjectByPredicate(sOrdersImdg.predicateBuilder().and(
@ -145,28 +168,36 @@ public class Application extends MessageCracker implements quickfix.Application,
sOrders.setClassCode(exDestination.getValue()); sOrders.setClassCode(exDestination.getValue());
sOrders.setTradeDate(transactTime.getValue().toLocalDate()); sOrders.setTradeDate(transactTime.getValue().toLocalDate());
sOrders.setSecCode(securityID.getValue()); if (executionReport.isSetSecurityID())
sOrders.setPrice(BigDecimal.valueOf(price.getValue())); sOrders.setSecCode(executionReport.getSecurityID().getValue());
sOrders.setQty(BigDecimal.valueOf(orderQty.getValue())); if (executionReport.isSetPrice())
sOrders.setAccount(account.getValue()); sOrders.setPrice(BigDecimal.valueOf(executionReport.getPrice().getValue()));
sOrders.setSettleCode(settlType.getValue()); if (executionReport.isSetOrderQty())
sOrders.setPriceCurrency(currency.getValue()); sOrders.setQty(BigDecimal.valueOf(executionReport.getOrderQty().getValue()));
sOrders.setState(String.valueOf(ordStatus.getValue())); if (executionReport.isSetAccount())
sOrders.setAccount(executionReport.getAccount().getValue());
if (executionReport.isSetSettlType())
sOrders.setSettleCode(executionReport.getSettlType().getValue());
if (executionReport.isSetCurrency())
sOrders.setPriceCurrency(executionReport.getCurrency().getValue());
sOrders.setState(String.valueOf(ordStatus.getValue()));
if (ordStatus.valueEquals(OrdStatus.NEW)) { if (ordStatus.valueEquals(OrdStatus.NEW)) {
sOrders.setOrderDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC)); sOrders.setOrderDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC));
} else if (ordStatus.valueEquals(OrdStatus.CANCELED)) { } else if (ordStatus.valueEquals(OrdStatus.CANCELED)) {
sOrders.setWithdrawDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC)); sOrders.setWithdrawDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC));
} }
sOrders.setCreated(Instant.now());
sOrdersImdg.insert(sOrders); sOrdersImdg.insert(sOrders);
} else { } else {
sOrders.setState(String.valueOf(ordStatus.getValue()));
if (ordStatus.valueEquals(OrdStatus.NEW)) { if (ordStatus.valueEquals(OrdStatus.NEW)) {
sOrders.setOrderDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC)); sOrders.setOrderDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC));
} else if (ordStatus.valueEquals(OrdStatus.CANCELED)) { } else if (ordStatus.valueEquals(OrdStatus.CANCELED)) {
sOrders.setWithdrawDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC)); sOrders.setWithdrawDateTime(transactTime.getValue().toInstant(ZoneOffset.UTC));
} }
sOrders.setUpdated(Instant.now());
sOrdersImdg.update(sOrders); sOrdersImdg.update(sOrders);
} }
@ -175,7 +206,7 @@ public class Application extends MessageCracker implements quickfix.Application,
updateRequest.addId(orderNum); updateRequest.addId(orderNum);
kafkaSender.sendRequestToQueue(Consts.ORDER_CURRENCY_STATUS_UPDATE_FIX, updateRequest); kafkaSender.sendRequestToQueue(Consts.ORDER_CURRENCY_STATUS_UPDATE_FIX, updateRequest);
} }
} // }
transaction.commitTransaction(); transaction.commitTransaction();
} catch (Exception e) { } catch (Exception e) {
log.error(executionReport.toString(), e); log.error(executionReport.toString(), e);
@ -200,7 +231,7 @@ public class Application extends MessageCracker implements quickfix.Application,
newOrderSingle.setField(new ContraBroker(orderCurrency.getCounterTradingCode())); newOrderSingle.setField(new ContraBroker(orderCurrency.getCounterTradingCode()));
newOrderSingle.set(new SettlType(orderCurrency.getSettleCode())); newOrderSingle.set(new SettlType(orderCurrency.getSettleCode()));
newOrderSingle.setField(new StringField(5018, orderCurrency.getTradingCode())); // FirmId newOrderSingle.setField(new StringField(5018, orderCurrency.getTradingCode())); // FirmId
newOrderSingle.set(new HandlInst(HandlInst.AUTOMATED_EXECUTION_ORDER_PRIVATE_NO_BROKER_INTERVENTION));
boolean result = Session.sendToTarget(newOrderSingle, fixSessionID); boolean result = Session.sendToTarget(newOrderSingle, fixSessionID);
} catch (SessionNotFound sessionNotFound) { } catch (SessionNotFound sessionNotFound) {
log.error(orderCurrency.toString(), sessionNotFound); log.error(orderCurrency.toString(), sessionNotFound);

View file

@ -6,7 +6,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import ru.spcex.clearing.gatewayapi.config.deserializers.LocalDateDeserializer; import ru.spcex.clearing.gatewayapi.config.deserializers.LocalDateDeserializer;
import ru.spcex.clearing.gatewayapi.config.deserializers.LocalDateTimeV2Deserializer; import ru.spcex.clearing.gatewayapi.config.deserializers.LocalDateTimeDeserializer;
public class CurrencyRate { public class CurrencyRate {
@ -32,11 +32,11 @@ public class CurrencyRate {
private BigDecimal price; private BigDecimal price;
@JsonDeserialize(using = LocalDateTimeV2Deserializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonProperty("update_date_time") @JsonProperty("update_date_time")
private LocalDateTime updateDateTime; private LocalDateTime updateDateTime;
@JsonDeserialize(using = LocalDateTimeV2Deserializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonProperty("source_date_time") @JsonProperty("source_date_time")
private LocalDateTime sourceDateTime; private LocalDateTime sourceDateTime;

View file

@ -31,7 +31,7 @@ public class RiskParameterMapStore extends TemplateMapStore<RiskParameter> {
public String[] getFields() { public String[] getFields() {
return new String[]{ return new String[]{
"ID", "VALUE", "SETTLEMENT_TYPE", "CURRENCY_PAIR", "CURRENCY_PAIR_ID", "CREATED_AT", "UPDATED_AT", "WORKFLOW_STATUS", "ID", "VALUE", "SETTLEMENT_TYPE", "CURRENCY_PAIR", "CURRENCY_PAIR_ID", "CREATED_AT", "UPDATED_AT", "WORKFLOW_STATUS",
"QUOTE_VALUE" "QUOTE_VALUE", "PENALTY_VALUE"
}; };
} }
@ -47,6 +47,7 @@ public class RiskParameterMapStore extends TemplateMapStore<RiskParameter> {
object.setUpdated(getInstantFromTimestamp(resultSet, "UPDATED_AT")); object.setUpdated(getInstantFromTimestamp(resultSet, "UPDATED_AT"));
object.setWorkflowStatus(resultSet.getObject("WORKFLOW_STATUS", String.class)); object.setWorkflowStatus(resultSet.getObject("WORKFLOW_STATUS", String.class));
object.setQuoteValue(resultSet.getObject("QUOTE_VALUE", BigDecimal.class)); object.setQuoteValue(resultSet.getObject("QUOTE_VALUE", BigDecimal.class));
object.setPenaltyValue(resultSet.getObject("PENALTY_VALUE", BigDecimal.class));
return object; return object;
} }
@ -61,7 +62,8 @@ public class RiskParameterMapStore extends TemplateMapStore<RiskParameter> {
TimeUtil.toDateFromInstant(object.getCreated()), TimeUtil.toDateFromInstant(object.getCreated()),
TimeUtil.toDateFromInstant(object.getUpdated()), TimeUtil.toDateFromInstant(object.getUpdated()),
object.getWorkflowStatus(), object.getWorkflowStatus(),
object.getQuoteValue() object.getQuoteValue(),
object.getPenaltyValue()
}; };
return args; return args;
} }

View file

@ -42,6 +42,7 @@ public class RiskParameterFacade implements IClearingFacade {
riskParameter.setSettlementType(request.getSettlementType()); riskParameter.setSettlementType(request.getSettlementType());
riskParameter.setValue(request.getValue()); riskParameter.setValue(request.getValue());
riskParameter.setQuoteValue(request.getQuoteValue()); riskParameter.setQuoteValue(request.getQuoteValue());
riskParameter.setPenaltyValue(request.getPenaltyValue());
riskParameter.setWorkflowStatus(request.getWorkflowStatus()); riskParameter.setWorkflowStatus(request.getWorkflowStatus());
riskParameter.setCreated(Instant.now()); riskParameter.setCreated(Instant.now());
@ -55,6 +56,7 @@ public class RiskParameterFacade implements IClearingFacade {
riskParameterForUpdate.setValue(request.getValue()); riskParameterForUpdate.setValue(request.getValue());
riskParameterForUpdate.setQuoteValue(request.getQuoteValue()); riskParameterForUpdate.setQuoteValue(request.getQuoteValue());
riskParameterForUpdate.setPenaltyValue(request.getPenaltyValue());
riskParameterForUpdate.setWorkflowStatus(request.getWorkflowStatus()); riskParameterForUpdate.setWorkflowStatus(request.getWorkflowStatus());
riskParameterImdg.update(riskParameterForUpdate); riskParameterImdg.update(riskParameterForUpdate);

View file

@ -2,6 +2,8 @@ package ru.spcex.platform.imdg.validation.rule;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.spcex.platform.classes.base.SpcexObjectBase; import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
@ -10,6 +12,7 @@ import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.validation.IValidationRule; import ru.spcex.platform.utils.validation.IValidationRule;
public class PresentByPredicate<T> implements IValidationRule<ImdgValidationContext<T>> { public class PresentByPredicate<T> implements IValidationRule<ImdgValidationContext<T>> {
private static final Logger log = LoggerFactory.getLogger(PresentByPredicate.class);
private final String mapName; private final String mapName;
private final Function<T, EnumMessage> errorProvider; private final Function<T, EnumMessage> errorProvider;
@ -42,8 +45,10 @@ public class PresentByPredicate<T> implements IValidationRule<ImdgValidationCont
if (obj == null) { if (obj == null) {
EnumMessage errMsg = errorProvider.apply(validatedObject); EnumMessage errMsg = errorProvider.apply(validatedObject);
log.debug("object not found by predicate: {}", predicate);
return Optional.of(errMsg); return Optional.of(errMsg);
} }
log.debug("object found by predicate [{}]: {}.id={}", predicate, mapName, obj.getId());
if (storeEnabled != null) { if (storeEnabled != null) {
context.storeObject(storeEnabled, obj); context.storeObject(storeEnabled, obj);

View file

@ -10,6 +10,8 @@ public class RiskParameterNewRequest {
@JsonProperty @JsonProperty
private BigDecimal quoteValue; private BigDecimal quoteValue;
@JsonProperty @JsonProperty
private BigDecimal penaltyValue;
@JsonProperty
private String settlementType; private String settlementType;
@JsonProperty @JsonProperty
private Long currencyPairId; private Long currencyPairId;
@ -55,4 +57,12 @@ public class RiskParameterNewRequest {
public void setWorkflowStatus(String status) { public void setWorkflowStatus(String status) {
this.workflowStatus = status; this.workflowStatus = status;
} }
public BigDecimal getPenaltyValue() {
return penaltyValue;
}
public void setPenaltyValue(BigDecimal penaltyValue) {
this.penaltyValue = penaltyValue;
}
} }

View file

@ -12,6 +12,8 @@ public class RiskParameterUpdateRequest {
@JsonProperty @JsonProperty
private BigDecimal quoteValue; private BigDecimal quoteValue;
@JsonProperty @JsonProperty
private BigDecimal penaltyValue;
@JsonProperty
private String workflowStatus; private String workflowStatus;
public Long getId() { public Long getId() {
@ -45,4 +47,12 @@ public class RiskParameterUpdateRequest {
public void setWorkflowStatus(String status) { public void setWorkflowStatus(String status) {
this.workflowStatus = status; this.workflowStatus = status;
} }
public BigDecimal getPenaltyValue() {
return penaltyValue;
}
public void setPenaltyValue(BigDecimal penaltyValue) {
this.penaltyValue = penaltyValue;
}
} }