Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
ialbert 2024-04-17 18:36:17 +03:00
commit e58a19a511
19 changed files with 161 additions and 90 deletions

View file

@ -103,6 +103,7 @@
<instrumentType id="3" code="EQTY" name="Акция"/>
<instrumentType id="4" code="BOND" name="Облигация"/>
<instrumentType id="5" code="MFND" name="ПАИ"/>
<instrumentType id="6" code="CURR" name="Валютная пара"/>
<termType id="1" code="S" name="Срочный"/>
<termType id="2" code="K" name="Комбинированный"/>
<termType id="3" code="V" name="До востребования"/>

View file

@ -208,6 +208,8 @@ INSERT INTO INSTRUMENT_TYPE_DICTIONARY(ID, CODE, NAME) values (4, 'BOND', 'Об
INSERT INTO INSTRUMENT_TYPE_DICTIONARY(ID, CODE, NAME) values (5, 'MFND', 'ПАИ') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO INSTRUMENT_TYPE_DICTIONARY(ID, CODE, NAME) values (6, 'CURR', 'Валютная пара') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO TERM_TYPE_DICTIONARY(ID, CODE, NAME) values (1, 'S', 'Срочный') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO TERM_TYPE_DICTIONARY(ID, CODE, NAME) values (2, 'K', 'Комбинированный') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;

View file

@ -5,13 +5,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
@ -20,6 +20,8 @@ import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.integration.sftp.session.SftpFileInfo;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import ru.spcex.clearing.dbf.exporter.config.settings.ExportDBFServiceSettings;
import java.io.File;
@ -30,7 +32,7 @@ import static org.springframework.integration.file.remote.gateway.AbstractRemote
@Configuration
public class SFTPConfig {
protected Logger log = LoggerFactory.getLogger(getClass());
protected static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
@Bean
public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory(ExportDBFServiceSettings settings) {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
@ -44,10 +46,9 @@ public class SFTPConfig {
@Bean
@ServiceActivator(inputChannel = "toSftpChannel")
public MessageHandler handler(SessionFactory<ChannelSftp.LsEntry> sessionFactory, ExportDBFServiceSettings settings) {
public MessageHandler handler(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
SftpMessageHandler handler = new SftpMessageHandler(sessionFactory);
handler.setRemoteDirectoryExpression(new LiteralExpression(settings.getStore().getOutDir()));
log.info("Configure sftp output directory \"{}\"", settings.getStore().getOutDir());
handler.setRemoteDirectoryExpression(EXPRESSION_PARSER.parseExpression("headers['path']"));
handler.setAutoCreateDirectory(true);
handler.setFileNameGenerator(message -> {
if (message.getPayload() instanceof File) {
@ -62,38 +63,31 @@ public class SFTPConfig {
@MessagingGateway
public interface DbfGateway {
@Gateway(requestChannel = "toSftpChannel")
void sendToSftp(File file);
void sendToSftp(@Payload File file, @Header("path") String path);
@Gateway(requestChannel = "listSftpChannel")
List<SftpFileInfo> listFiles(String dir);
List<SftpFileInfo> listFiles(@Payload String dir);
}
@Bean
public MessageChannel listSftpChannel(SessionFactory<ChannelSftp.LsEntry> sessionFactory, ExportDBFServiceSettings settings) {
public MessageChannel listSftpChannel(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
DirectChannel dc = new DirectChannel();
dc.subscribe(handlerList(sessionFactory, settings));
dc.subscribe(handlerList(sessionFactory));
return dc;
}
@Bean
public MessageChannel toSftpChannel(SessionFactory<ChannelSftp.LsEntry> sessionFactory, ExportDBFServiceSettings settings) {
public MessageChannel toSftpChannel(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
DirectChannel dc = new DirectChannel();
dc.subscribe(handler(sessionFactory, settings));
dc.subscribe(handler(sessionFactory));
return dc;
}
@Bean
@ServiceActivator(inputChannel = "listSftpChannel")
public MessageHandler handlerList(SessionFactory<ChannelSftp.LsEntry> sessionFactory, ExportDBFServiceSettings settings) {
String expression = "'/%s'".formatted(settings.getStore().getOutDir());
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sessionFactory, LS.getCommand(), expression);
public MessageHandler handlerList(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
SftpOutboundGateway sftpOutboundGateway = new SftpOutboundGateway(sessionFactory, LS.getCommand(), "payload");
sftpOutboundGateway.setOption(AbstractRemoteFileOutboundGateway.Option.RECURSIVE);
return sftpOutboundGateway;
}
@Bean
public IntegrationFlow sftpOutboundListFlow(SessionFactory<ChannelSftp.LsEntry> sessionFactory, ExportDBFServiceSettings settings) {
return IntegrationFlows.from("listSftpChannel")
.handle(new SftpOutboundGateway(sessionFactory, "ls", "payload")
).get();
}
}

View file

@ -1,5 +1,7 @@
package ru.spcex.clearing.dbf.exporter.config.settings;
import java.util.Map;
public class Store {
private String outDir;
@ -8,6 +10,7 @@ public class Store {
private String password;
private String serverIp;
private int serverPort;
private Map<String, String> outPayValDir;
public String getUser() {
return user;
@ -56,4 +59,12 @@ public class Store {
public void setLocalTempDir(String localTempDir) {
this.localTempDir = localTempDir;
}
public Map<String, String> getOutPayValDir() {
return outPayValDir;
}
public void setOutPayValDir(Map<String, String> outPayValDir) {
this.outPayValDir = outPayValDir;
}
}

View file

@ -4,15 +4,15 @@ import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFWriter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import ru.clearing.classes.statics.data.sdf.*;
import ru.spcex.clearing.dbf.exporter.config.SFTPConfig;
import ru.spcex.clearing.dbf.exporter.config.settings.ExportDBFServiceSettings;
import ru.spcex.clearing.dbf.exporter.exceptions.ConfigException;
import ru.spcex.clearing.dbf.exporter.logic.data.ResultContainer;
import ru.spcex.clearing.dbf.exporter.logic.data.enums.StageResult;
import ru.spcex.clearing.dbf.exporter.logic.data.enums.Table;
import ru.spcex.clearing.dbf.exporter.services.converters.*;
import ru.spcex.clearing.dbf.exporter.services.converters.DFConverter;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
@ -20,6 +20,8 @@ import java.io.File;
import java.nio.charset.Charset;
import java.util.*;
import static ru.spcex.clearing.dbf.exporter.services.converters.DFConverter.ruCurrency;
/**
* Выгрузка данных из мапы hazelcast и их запись в файлы
*/
@ -28,15 +30,8 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
private final ExportDBFServiceSettings settings;
private final ImdgProvider imdgProvider;
private final SFTPConfig.DbfGateway gateway;
private final List<DFConverter> converters;
private final S_DF02_Converter s_df02_converter;
private final S_DF03_Converter s_df03_converter;
private final S_DF05_Converter s_df05_converter;
private final S_DF07_Converter s_df07_converter;
private final S_DF51_Converter s_df51_converter;
private final S_DF53_Converter s_df53_converter;
private final S_DF54_Converter s_df54_converter;
private final S_DF56_Converter s_df56_converter;
private final Map<Table, DBFField[]> dbfFieldsForTable = new HashMap<>();
private Charset dbfCharset;
@ -44,25 +39,11 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
public ExportFromHazelcast(ExportDBFServiceSettings settings,
ImdgProvider imdgProvider,
SFTPConfig.DbfGateway gateway,
S_DF02_Converter s_df02_converter,
S_DF03_Converter s_df03_converter,
S_DF07_Converter s_df07_converter,
S_DF05_Converter s_df05_converter,
S_DF51_Converter s_df51_converter,
S_DF54_Converter s_df54_converter,
S_DF53_Converter s_df53_converter,
S_DF56_Converter s_df56_converter) {
List<DFConverter> converters) {
this.settings = settings;
this.imdgProvider = imdgProvider;
this.gateway = gateway;
this.s_df02_converter = s_df02_converter;
this.s_df03_converter = s_df03_converter;
this.s_df07_converter = s_df07_converter;
this.s_df05_converter = s_df05_converter;
this.s_df51_converter = s_df51_converter;
this.s_df54_converter = s_df54_converter;
this.s_df53_converter = s_df53_converter;
this.s_df56_converter = s_df56_converter;
this.converters = converters;
}
@Override
@ -75,8 +56,9 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
File dbfFile = resultContainer.getFileForExport();
boolean writeOk = false;
boolean emptyMap = true;
String currency = null;
String path = null;
try (DBFWriter dbfWriter = new DBFWriter(dbfFile, dbfCharset)) {
dbfWriter.setFields(dbfFieldsForTable.get(table));
Collection<? extends SpcexObjectBase> tableRows;
if (resultContainer.getGroupId() != null) {
Map<String, Long> queryParams = Map.of("generationId", resultContainer.getGroupId());
@ -97,18 +79,20 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
((ArrayList<SpcexObjectBase>) tableRows).sort(Comparator.comparing(SpcexObjectBase::getId));
}
for (SpcexObjectBase value : tableRows) {
Object[] values;
if (value instanceof SDf02 sDf02Value) values = s_df02_converter.toObjectArray(sDf02Value);
else if (value instanceof SDf03 sDf03Value) values = s_df03_converter.toObjectArray(sDf03Value);
else if (value instanceof SDf05 sDf05Value) values = s_df05_converter.toObjectArray(sDf05Value);
else if (value instanceof SDf07 sDf07Value) values = s_df07_converter.toObjectArray(sDf07Value);
else if (value instanceof SDf51 sDf51Value) values = s_df51_converter.toObjectArray(sDf51Value);
else if (value instanceof SDf53 sDf53Value) values = s_df53_converter.toObjectArray(sDf53Value);
else if (value instanceof SDf54 sDf54Value) values = s_df54_converter.toObjectArray(sDf54Value);
else if (value instanceof SDf56 sDf56Value) values = s_df56_converter.toObjectArray(sDf56Value);
else throw new Exception("Get unknown object from imdg. Class: " + value.getClass().getSimpleName());
dbfWriter.addRecord(values);
DFConverter currentConverter = converters.stream().filter(c -> c.canProcessing(value)).findFirst()
.orElseThrow(() -> new Exception("Get unknown object from imdg. Class: " + value.getClass().getSimpleName()));
if (currency == null) {
currency = currentConverter.getCurrency(value);
dbfWriter.setFields(currentConverter.getDBFHeaders());
}
dbfWriter.addRecord(currentConverter.toObjectArray(value));
}
if (CurrencyCode.isRub(currency))
path = settings.getStore().getOutPayValDir().get(ruCurrency);
else path = settings.getStore().getOutPayValDir().get(currency);
if (path == null)
throw new Exception("Get unknown currency=" + currency + ", need will be adding settings like 'export-dbf-service.store.out-pay-val-dir.RUB=/RUB' and restart app");
writeOk = true;
emptyMap = tableRows.isEmpty();
} catch (Exception e) {
@ -124,7 +108,7 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
}
} else {
log.debug("uuid {}, send to SFTP", resultContainer.getUuid());
gateway.sendToSftp(dbfFile);
gateway.sendToSftp(dbfFile, path);
}
}
@ -137,24 +121,9 @@ public class ExportFromHazelcast extends Stage implements InitializingBean {
*/
@Override
public void afterPropertiesSet() {
initExportFileStructure();
initDBFCharset();
}
/**
* Инициализация структуры выходных файлов (заголовки столбцов)
*/
private void initExportFileStructure() {
dbfFieldsForTable.put(Table.S_DF02, s_df02_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF03, s_df03_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF05, s_df05_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF07, s_df07_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF51, s_df51_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF53, s_df53_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF54, s_df54_converter.getDBFHeaders());
dbfFieldsForTable.put(Table.S_DF56, s_df56_converter.getDBFHeaders());
}
private void initDBFCharset() {
try {
dbfCharset = Charset.forName(settings.getCommon().getEncoding());

View file

@ -15,10 +15,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
/**
* Создание DBF файла
@ -38,8 +35,15 @@ public class PrepareDBFFile extends Stage implements InitializingBean {
@Override
public StageResult process(ResultContainer resultContainer) {
Objects.requireNonNull(resultContainer.getTableForExport());
List<SftpFileInfo> files = gateway.listFiles(settings.getStore().getOutDir());
log.debug("From sFTP dir \"{}\" list {} file names.", settings.getStore().getOutDir(), files.size());
if (settings.getStore().getOutPayValDir() == null || settings.getStore().getOutPayValDir().size() == 0) {
log.error("No SFTP scanning directories, need will be adding settings like 'export-dbf-service.store.out-pay-val-dir.RUB=/RUB' and restart app");
return StageResult.ERROR;
}
List<SftpFileInfo> files = new ArrayList<>();
for (String path : settings.getStore().getOutPayValDir().values()) {
files.addAll(gateway.listFiles(path));
}
log.info("There are {} files on the sFTP server in the search directories.", files.size());
Table table = resultContainer.getTableForExport();
LocalDateTime currentDateTime = LocalDateTime.now();
resultContainer.setRegistrationDateTime(currentDateTime);
@ -53,8 +57,8 @@ public class PrepareDBFFile extends Stage implements InitializingBean {
return StageResult.ERROR;
}
resultContainer.setFileForExport(dbfFile);
log.debug("uuid {}. GenerationId {} will be export to temp file \"{}\"", resultContainer.getUuid(),
resultContainer.getGroupId(), dbfFile);
log.info("uuid {}. Rows from {} with generationId {} will be export to temp file \"{}\"", resultContainer.getUuid(),
resultContainer.getTableForExport().getFilePrefix(), resultContainer.getGroupId(), dbfFile);
return StageResult.OK;
}

View file

@ -2,6 +2,7 @@ package ru.spcex.clearing.dbf.exporter.services.converters;
import com.linuxense.javadbf.DBFField;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.enumeration.CurrencyCode;
import java.math.BigDecimal;
import java.sql.Date;
@ -12,9 +13,15 @@ import java.time.LocalDate;
import java.time.LocalTime;
public abstract class DFConverter<T extends SpcexObjectBase> {
public final static String ruCurrency = CurrencyCode.RUB.getKey();
public Class<T> clazz;
public DFConverter (Class<T> clazz) {
this.clazz = clazz;
}
public abstract Object[] toObjectArray(T entity);
public abstract DBFField[] getDBFHeaders();
public abstract String getCurrency(T entity);
/**
* Приведение типов в соответствие (для записи)
* @param src исходный объект
@ -36,4 +43,8 @@ public abstract class DFConverter<T extends SpcexObjectBase> {
if (s == null) return null;
return new BigDecimal(s).setScale(0);
}
public boolean canProcessing(SpcexObjectBase entity) {
return entity.getClass().equals(clazz);
}
}

View file

@ -10,6 +10,10 @@ import java.util.List;
@Service
public class S_DF02_Converter extends DFConverter<SDf02> {
public S_DF02_Converter() {
super(SDf02.class);
}
@Override
public Object[] toObjectArray(SDf02 entity) {
List<Object> values = new LinkedList<>();
@ -48,4 +52,9 @@ public class S_DF02_Converter extends DFConverter<SDf02> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf02 entity) {
return entity.getCurr_code();
}
}

View file

@ -10,6 +10,10 @@ import java.util.List;
@Service
public class S_DF03_Converter extends DFConverter<SDf03> {
public S_DF03_Converter() {
super(SDf03.class);
}
@Override
public Object[] toObjectArray(SDf03 entity) {
List<Object> values = new LinkedList<>();
@ -64,4 +68,9 @@ public class S_DF03_Converter extends DFConverter<SDf03> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf03 entity) {
return entity.getPay_val();
}
}

View file

@ -11,6 +11,10 @@ import java.util.List;
@Service
public class S_DF05_Converter extends DFConverter<SDf05> {
public S_DF05_Converter() {
super(SDf05.class);
}
@Override
public Object[] toObjectArray(SDf05 entity) {
List<Object> values = new LinkedList<>();
@ -32,4 +36,9 @@ public class S_DF05_Converter extends DFConverter<SDf05> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf05 entity) {
return ruCurrency;
}
}

View file

@ -10,6 +10,10 @@ import java.util.List;
@Service
public class S_DF07_Converter extends DFConverter<SDf07> {
public S_DF07_Converter() {
super(SDf07.class);
}
@Override
public Object[] toObjectArray(SDf07 entity) {
List<Object> values = new LinkedList<>();
@ -59,4 +63,9 @@ public class S_DF07_Converter extends DFConverter<SDf07> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf07 entity) {
return entity.getPay_val();
}
}

View file

@ -11,6 +11,10 @@ import java.util.List;
@Service
public class S_DF51_Converter extends DFConverter<SDf51> {
public S_DF51_Converter() {
super(SDf51.class);
}
@Override
public Object[] toObjectArray(SDf51 entity) {
List<Object> values = new LinkedList<>();
@ -27,6 +31,11 @@ public class S_DF51_Converter extends DFConverter<SDf51> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf51 entity) {
return ruCurrency;
}
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");

View file

@ -10,6 +10,10 @@ import java.util.List;
@Service
public class S_DF53_Converter extends DFConverter<SDf53> {
public S_DF53_Converter() {
super(SDf53.class);
}
@Override
public Object[] toObjectArray(SDf53 entity) {
List<Object> values = new LinkedList<>();
@ -34,4 +38,9 @@ public class S_DF53_Converter extends DFConverter<SDf53> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf53 entity) {
return ruCurrency;
}
}

View file

@ -11,6 +11,10 @@ import java.util.List;
@Service
public class S_DF54_Converter extends DFConverter<SDf54> {
public S_DF54_Converter() {
super(SDf54.class);
}
@Override
public Object[] toObjectArray(SDf54 entity) {
List<Object> values = new LinkedList<>();
@ -113,4 +117,9 @@ public class S_DF54_Converter extends DFConverter<SDf54> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf54 entity) {
return entity.getPay_val();
}
}

View file

@ -10,6 +10,10 @@ import java.util.List;
@Service
public class S_DF56_Converter extends DFConverter<SDf56> {
public S_DF56_Converter() {
super(SDf56.class);
}
@Override
public Object[] toObjectArray(SDf56 entity) {
List<Object> values = new LinkedList<>();
@ -32,4 +36,9 @@ public class S_DF56_Converter extends DFConverter<SDf56> {
return dbfFields.toArray(DBFField[]::new);
}
@Override
public String getCurrency(SDf56 entity) {
return ruCurrency;
}
}

View file

@ -8,7 +8,9 @@ export-dbf-service.common.encoding=cp866
export-dbf-service.common.threads-count=10
export-dbf-service.store.local-temp-dir=D:\\docs and T3\\clearing\\dbf\\
export-dbf-service.store.out-dir=DocOut
# код валюты должен быть в верхнем регистре, например для рублей - "RUB" (*.RUB=export/rub/).RUB=export/rub/
export-dbf-service.store.out-pay-val-dir.RUB=export/rub/
export-dbf-service.store.out-pay-val-dir.EUR=export/eur/
export-dbf-service.store.user:tester
export-dbf-service.store.password=password
export-dbf-service.store.server-ip=10.230.238.53

View file

@ -5,7 +5,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.integration.sftp.session.SftpFileInfo;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@Configuration
@ -18,13 +17,13 @@ public class SFTPTestConfig {
public static class DGateway implements SFTPConfig.DbfGateway{
@Override
public void sendToSftp(File file) {
public void sendToSftp(File file, String path) {
}
@Override
public List<SftpFileInfo> listFiles(String dir) {
return new ArrayList<>();
return null;
}
}

View file

@ -27,6 +27,10 @@ public class MarketMapStore extends TemplateMapStore<Market> {
return "MARKET";
}
public String[] getIndexingField() {
return new String[]{"code"};
}
@Override
public String[] getFields() {
return new String[]{

View file

@ -6,7 +6,9 @@ public enum InstrumentType implements IEnumKey {
BOND("BOND"), // (облигации)
EQTY("EQTY"), // (акции)
RATE("RATE"), // Инструмент Денежного рынка
CRNC ("CRNC")
CRNC("CRNC"), // Валюта
MFND("MFND"),
CURR("CURR")
;
private final String key;