Merge branch 'develop' of 10.10.31.70:/ervu-lkrp-ul into develop

This commit is contained in:
Zaripov Emil 2025-12-03 18:57:27 +03:00
commit 25ac2bf590
30 changed files with 1986 additions and 215 deletions

View file

@ -0,0 +1,14 @@
package ervu.enums;
import ru.cg.webbpm.modules.webkit.annotations.Model;
/**
* @author gulnaz
*/
@Model
public enum ExcerptStatus {
NONE,
PENDING,
READY,
NOT_FOUND
}

View file

@ -7,6 +7,7 @@ package ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.DefaultCatalog;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.Databasechangelog;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.Databasechangeloglock;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.ExcerptHistory;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.InteractionLog;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.OkopfRecords;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.OrganizationAllowed;
@ -43,6 +44,11 @@ public class Public extends SchemaImpl {
*/
public final Databasechangeloglock DATABASECHANGELOGLOCK = Databasechangeloglock.DATABASECHANGELOGLOCK;
/**
* The table <code>public.excerpt_history</code>.
*/
public final ExcerptHistory EXCERPT_HISTORY = ExcerptHistory.EXCERPT_HISTORY;
/**
* The table <code>public.interaction_log</code>.
*/
@ -81,6 +87,7 @@ public class Public extends SchemaImpl {
return Arrays.asList(
Databasechangelog.DATABASECHANGELOG,
Databasechangeloglock.DATABASECHANGELOGLOCK,
ExcerptHistory.EXCERPT_HISTORY,
InteractionLog.INTERACTION_LOG,
OkopfRecords.OKOPF_RECORDS,
OrganizationAllowed.ORGANIZATION_ALLOWED,

View file

@ -6,6 +6,7 @@ package ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.Databasechangelog;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.Databasechangeloglock;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.ExcerptHistory;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.InteractionLog;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.OkopfRecords;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.OrganizationAllowed;
@ -28,6 +29,11 @@ public class Tables {
*/
public static final Databasechangeloglock DATABASECHANGELOGLOCK = Databasechangeloglock.DATABASECHANGELOGLOCK;
/**
* The table <code>public.excerpt_history</code>.
*/
public static final ExcerptHistory EXCERPT_HISTORY = ExcerptHistory.EXCERPT_HISTORY;
/**
* The table <code>public.interaction_log</code>.
*/

View file

@ -0,0 +1,243 @@
/*
* This file is generated by jOOQ.
*/
package ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.Public;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.records.ExcerptHistoryRecord;
import java.sql.Timestamp;
import java.util.Collection;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Identity;
import org.jooq.Name;
import org.jooq.PlainSQL;
import org.jooq.QueryPart;
import org.jooq.SQL;
import org.jooq.Schema;
import org.jooq.Select;
import org.jooq.Stringly;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableOptions;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.jooq.impl.TableImpl;
/**
* This class is generated by jOOQ.
*/
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class ExcerptHistory extends TableImpl<ExcerptHistoryRecord> {
private static final long serialVersionUID = 1L;
/**
* The reference instance of <code>public.excerpt_history</code>
*/
public static final ExcerptHistory EXCERPT_HISTORY = new ExcerptHistory();
/**
* The class holding records for this type
*/
@Override
public Class<ExcerptHistoryRecord> getRecordType() {
return ExcerptHistoryRecord.class;
}
/**
* The column <code>public.excerpt_history.id</code>.
*/
public final TableField<ExcerptHistoryRecord, Long> ID = createField(DSL.name("id"), SQLDataType.BIGINT.nullable(false).identity(true), this, "");
/**
* The column <code>public.excerpt_history.ervu_id</code>.
*/
public final TableField<ExcerptHistoryRecord, String> ERVU_ID = createField(DSL.name("ervu_id"), SQLDataType.VARCHAR(36).nullable(false), this, "");
/**
* The column <code>public.excerpt_history.prn_oid</code>.
*/
public final TableField<ExcerptHistoryRecord, String> PRN_OID = createField(DSL.name("prn_oid"), SQLDataType.VARCHAR(10).nullable(false), this, "");
/**
* The column <code>public.excerpt_history.url</code>.
*/
public final TableField<ExcerptHistoryRecord, String> URL = createField(DSL.name("url"), SQLDataType.CLOB, this, "");
/**
* The column <code>public.excerpt_history.datetime</code>.
*/
public final TableField<ExcerptHistoryRecord, Timestamp> DATETIME = createField(DSL.name("datetime"), SQLDataType.TIMESTAMP(0).nullable(false).defaultValue(DSL.field(DSL.raw("now()"), SQLDataType.TIMESTAMP)), this, "");
/**
* The column <code>public.excerpt_history.status</code>.
*/
public final TableField<ExcerptHistoryRecord, String> STATUS = createField(DSL.name("status"), SQLDataType.VARCHAR(20), this, "");
private ExcerptHistory(Name alias, Table<ExcerptHistoryRecord> aliased) {
this(alias, aliased, (Field<?>[]) null, null);
}
private ExcerptHistory(Name alias, Table<ExcerptHistoryRecord> aliased, Field<?>[] parameters, Condition where) {
super(alias, null, aliased, parameters, DSL.comment(""), TableOptions.table(), where);
}
/**
* Create an aliased <code>public.excerpt_history</code> table reference
*/
public ExcerptHistory(String alias) {
this(DSL.name(alias), EXCERPT_HISTORY);
}
/**
* Create an aliased <code>public.excerpt_history</code> table reference
*/
public ExcerptHistory(Name alias) {
this(alias, EXCERPT_HISTORY);
}
/**
* Create a <code>public.excerpt_history</code> table reference
*/
public ExcerptHistory() {
this(DSL.name("excerpt_history"), null);
}
@Override
public Schema getSchema() {
return aliased() ? null : Public.PUBLIC;
}
@Override
public Identity<ExcerptHistoryRecord, Long> getIdentity() {
return (Identity<ExcerptHistoryRecord, Long>) super.getIdentity();
}
@Override
public ExcerptHistory as(String alias) {
return new ExcerptHistory(DSL.name(alias), this);
}
@Override
public ExcerptHistory as(Name alias) {
return new ExcerptHistory(alias, this);
}
@Override
public ExcerptHistory as(Table<?> alias) {
return new ExcerptHistory(alias.getQualifiedName(), this);
}
/**
* Rename this table
*/
@Override
public ExcerptHistory rename(String name) {
return new ExcerptHistory(DSL.name(name), null);
}
/**
* Rename this table
*/
@Override
public ExcerptHistory rename(Name name) {
return new ExcerptHistory(name, null);
}
/**
* Rename this table
*/
@Override
public ExcerptHistory rename(Table<?> name) {
return new ExcerptHistory(name.getQualifiedName(), null);
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory where(Condition condition) {
return new ExcerptHistory(getQualifiedName(), aliased() ? this : null, null, condition);
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory where(Collection<? extends Condition> conditions) {
return where(DSL.and(conditions));
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory where(Condition... conditions) {
return where(DSL.and(conditions));
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory where(Field<Boolean> condition) {
return where(DSL.condition(condition));
}
/**
* Create an inline derived table from this table
*/
@Override
@PlainSQL
public ExcerptHistory where(SQL condition) {
return where(DSL.condition(condition));
}
/**
* Create an inline derived table from this table
*/
@Override
@PlainSQL
public ExcerptHistory where(@Stringly.SQL String condition) {
return where(DSL.condition(condition));
}
/**
* Create an inline derived table from this table
*/
@Override
@PlainSQL
public ExcerptHistory where(@Stringly.SQL String condition, Object... binds) {
return where(DSL.condition(condition, binds));
}
/**
* Create an inline derived table from this table
*/
@Override
@PlainSQL
public ExcerptHistory where(@Stringly.SQL String condition, QueryPart... parts) {
return where(DSL.condition(condition, parts));
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory whereExists(Select<?> select) {
return where(DSL.exists(select));
}
/**
* Create an inline derived table from this table
*/
@Override
public ExcerptHistory whereNotExists(Select<?> select) {
return where(DSL.notExists(select));
}
}

View file

@ -0,0 +1,131 @@
/*
* This file is generated by jOOQ.
*/
package ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.records;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.ExcerptHistory;
import java.sql.Timestamp;
import org.jooq.impl.TableRecordImpl;
/**
* This class is generated by jOOQ.
*/
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class ExcerptHistoryRecord extends TableRecordImpl<ExcerptHistoryRecord> {
private static final long serialVersionUID = 1L;
/**
* Setter for <code>public.excerpt_history.id</code>.
*/
public void setId(Long value) {
set(0, value);
}
/**
* Getter for <code>public.excerpt_history.id</code>.
*/
public Long getId() {
return (Long) get(0);
}
/**
* Setter for <code>public.excerpt_history.ervu_id</code>.
*/
public void setErvuId(String value) {
set(1, value);
}
/**
* Getter for <code>public.excerpt_history.ervu_id</code>.
*/
public String getErvuId() {
return (String) get(1);
}
/**
* Setter for <code>public.excerpt_history.prn_oid</code>.
*/
public void setPrnOid(String value) {
set(2, value);
}
/**
* Getter for <code>public.excerpt_history.prn_oid</code>.
*/
public String getPrnOid() {
return (String) get(2);
}
/**
* Setter for <code>public.excerpt_history.url</code>.
*/
public void setUrl(String value) {
set(3, value);
}
/**
* Getter for <code>public.excerpt_history.url</code>.
*/
public String getUrl() {
return (String) get(3);
}
/**
* Setter for <code>public.excerpt_history.datetime</code>.
*/
public void setDatetime(Timestamp value) {
set(4, value);
}
/**
* Getter for <code>public.excerpt_history.datetime</code>.
*/
public Timestamp getDatetime() {
return (Timestamp) get(4);
}
/**
* Setter for <code>public.excerpt_history.status</code>.
*/
public void setStatus(String value) {
set(5, value);
}
/**
* Getter for <code>public.excerpt_history.status</code>.
*/
public String getStatus() {
return (String) get(5);
}
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
* Create a detached ExcerptHistoryRecord
*/
public ExcerptHistoryRecord() {
super(ExcerptHistory.EXCERPT_HISTORY);
}
/**
* Create a detached, initialised ExcerptHistoryRecord
*/
public ExcerptHistoryRecord(Long id, String ervuId, String prnOid, String url, Timestamp datetime, String status) {
super(ExcerptHistory.EXCERPT_HISTORY);
setId(id);
setErvuId(ervuId);
setPrnOid(prnOid);
setUrl(url);
setDatetime(datetime);
setStatus(status);
resetChangedOnNotNull();
}
}

View file

@ -6,6 +6,8 @@ import ervu.model.fileupload.FileInfo;
import ervu.model.fileupload.FileStatus;
import ervu.model.fileupload.UploadOrgInfo;
import ru.micord.ervu.audit.model.AuditActionRequest;
import ru.micord.ervu.kafka.model.ExcerptData;
import ru.micord.ervu.kafka.model.ExcerptResponse;
import ru.micord.ervu.kafka.model.OrgInfo;
/**
@ -21,4 +23,7 @@ public interface AuditService {
void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
int formatRegistry, String status, String s3FileUrl);
void processDownloadEvent(ExcerptData data, long fileSize, String fileName,
int formatRegistry, String status, String s3FileUrl);
}

View file

@ -20,6 +20,9 @@ import ru.micord.ervu.audit.model.*;
import ru.micord.ervu.audit.service.AuditKafkaPublisher;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.exception.JsonParsingException;
import ru.micord.ervu.kafka.model.Data;
import ru.micord.ervu.kafka.model.ExcerptData;
import ru.micord.ervu.kafka.model.ExcerptResponse;
import ru.micord.ervu.kafka.model.OrgInfo;
import ru.micord.ervu.security.esia.model.EsiaAccessToken;
import ru.micord.ervu.security.esia.service.UlDataService;
@ -123,15 +126,29 @@ public class BaseAuditService implements AuditService {
}
@Override
public void processDownloadEvent(
HttpServletRequest request, long fileSize, String fileName, int formatRegistry,
String status, String s3FileUrl) {
public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
int formatRegistry, String status, String s3FileUrl) {
String userAccountId = jwtTokenService.getUserAccountId(request);
String eventTime = DateUtils.getClientDateTimeWithZone(
DateUtils.getClientDateTimeWithZoneFromRequest(request));
publishDownloadEvent(getEsiaOrgId(request), userAccountId, eventTime, fileSize, fileName,
formatRegistry, status, s3FileUrl);
}
@Override
public void processDownloadEvent(ExcerptData data, long fileSize, String fileName,
int formatRegistry,String status, String s3FileUrl) {
String eventTime = DateUtils.getClientDateTimeWithZone(data.getTimeZone());
publishDownloadEvent(data.getOrgId(), data.getPrnOid(), eventTime, fileSize, fileName,
formatRegistry, status, s3FileUrl);
}
private void publishDownloadEvent(String esiaOrgId, String esiaPersonId, String eventTime,
long fileSize, String fileName, int formatRegistry, String status, String s3FileUrl) {
AuditDownloadEvent event = new AuditDownloadEvent(
getEsiaOrgId(request),
userAccountId,
DateUtils.getClientDateTimeWithZoneFromRequest(request),
esiaOrgId,
esiaPersonId,
eventTime,
AuditConstants.getDownloadType(formatRegistry),
fileName,
s3FileUrl,

View file

@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
import ru.micord.ervu.audit.config.AuditDisableCondition;
import ru.micord.ervu.audit.model.AuditActionRequest;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.kafka.model.ExcerptData;
import ru.micord.ervu.kafka.model.OrgInfo;
/**
@ -32,4 +33,8 @@ public class StubAuditService implements AuditService {
@Override
public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
int formatRegistry, String status, String s3FileUrl) {}
@Override
public void processDownloadEvent(ExcerptData data, long fileSize, String fileName, int formatRegistry,
String status, String s3FileUrl) {}
}

View file

@ -0,0 +1,140 @@
package ru.micord.ervu.controller;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.TimeZone;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import ervu.client.fileupload.WebDavClient;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.records.ExcerptHistoryRecord;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.dao.ExcerptHistoryDao;
import ru.micord.ervu.kafka.exception.ExcerptException;
import ru.micord.ervu.kafka.model.Data;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
import ru.micord.ervu.util.UrlUtils;
/**
* @author Eduard Tihomirov
*/
@RestController
@RequestMapping("/excerpt")
public class ExcerptController {
@Autowired
@Qualifier("fileTemplate")
private KafkaTemplate<String, String> kafkaTemplate;
@Value("${ervu.kafka.excerpt.request.topic}")
private String requestTopic;
@Autowired
private AuditService auditService;
@Autowired
private WebDavClient webDavClient;
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ExcerptHistoryDao excerptHistoryDao;
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public void requestExcerptUrl(@RequestBody Integer year, HttpServletRequest request) {
try {
String clientTimeZone = request.getHeader("Client-Time-Zone");
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
String ervuId = userIdsPair.getErvuId();
String prnOid = userIdsPair.getEsiaUserId();
Data data = new Data();
data.setErvuId(ervuId);
data.setPrnOid(prnOid);
String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId())
.getOffset().getId();
data.setTimeZone(offset);
data.setYear(year);
String messageId = UUID.randomUUID().toString();
ProducerRecord<String, String> record = new ProducerRecord<>(requestTopic,
objectMapper.writeValueAsString(data));
record.headers().add("messageId", messageId.getBytes(StandardCharsets.UTF_8));
kafkaTemplate.send(record).get();
excerptHistoryDao.delete(ervuId, prnOid);
excerptHistoryDao.insert(ervuId, prnOid);
}
catch (Exception e) {
throw new ExcerptException(e);
}
}
@GetMapping("/download")
public ResponseEntity<Resource> getExcerptFile(HttpServletRequest request) {
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
String ervuId = userIdsPair.getErvuId();
String prnOid = userIdsPair.getEsiaUserId();
String fileUrl = null;
String fileName = null;
long fileSize = 0;
try {
Optional<ExcerptHistoryRecord> historyRecord = excerptHistoryDao.get(ervuId, prnOid);
if (historyRecord.isEmpty()) {
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
return ResponseEntity.noContent().build();
}
else {
fileUrl = historyRecord.get().getUrl();
fileName = UrlUtils.extractFileNameFromUrl(fileUrl);
ResponseEntity<Resource> responseEntity = webDavClient.webDavDownloadFile(fileUrl);
if (responseEntity.getStatusCode().is2xxSuccessful()) {
fileSize = responseEntity.getHeaders().getContentLength();
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.SUCCESS_STATUS_TYPE, fileUrl
);
}
else {
excerptHistoryDao.delete(ervuId, prnOid);
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
}
return responseEntity;
}
}
catch (Exception e) {
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
throw new ExcerptException(e);
}
}
@GetMapping("/status")
public String getStatus() {
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
return excerptHistoryDao.getStatus(userIdsPair.getErvuId(), userIdsPair.getEsiaUserId());
}
}

View file

@ -0,0 +1,97 @@
package ru.micord.ervu.dao;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import ervu.enums.ExcerptStatus;
import ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.records.ExcerptHistoryRecord;
import org.jooq.DSLContext;
import org.jooq.impl.DSL;
import org.springframework.stereotype.Repository;
import static ervu_lkrp_ul.ervu_lkrp_ul.db_beans.public_.tables.ExcerptHistory.EXCERPT_HISTORY;
import static org.jooq.impl.DSL.coalesce;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.select;
/**
* @author gulnaz
*/
@Repository
public class ExcerptHistoryDao {
private final DSLContext dslContext;
public ExcerptHistoryDao(DSLContext dslContext) {
this.dslContext = dslContext;
}
public Optional<ExcerptHistoryRecord> get(String ervuId, String prnOid) {
return dslContext.select(EXCERPT_HISTORY.ID, EXCERPT_HISTORY.URL)
.from(EXCERPT_HISTORY)
.where(EXCERPT_HISTORY.ID.eq(
select(DSL.max(EXCERPT_HISTORY.ID))
.from(EXCERPT_HISTORY)
.where(EXCERPT_HISTORY.ERVU_ID.eq(ervuId)
.and(EXCERPT_HISTORY.PRN_OID.eq(prnOid)))
))
.fetchOptionalInto(ExcerptHistoryRecord.class);
}
public String getStatus(String ervuId, String prnOid) {
return dslContext.select(coalesce(
field(select(EXCERPT_HISTORY.STATUS)
.from(EXCERPT_HISTORY)
.where(EXCERPT_HISTORY.ERVU_ID.eq(ervuId)
.and(EXCERPT_HISTORY.PRN_OID.eq(prnOid)))
), ExcerptStatus.NONE.name()
)).fetchAnyInto(String.class);
}
public void insert(String ervuId, String prnOid) {
dslContext.insertInto(EXCERPT_HISTORY)
.set(EXCERPT_HISTORY.ERVU_ID, ervuId)
.set(EXCERPT_HISTORY.PRN_OID, prnOid)
.set(EXCERPT_HISTORY.STATUS, ExcerptStatus.PENDING.name())
.execute();
}
public void insert(String ervuId, String prnOid, String url, String status) {
dslContext.insertInto(EXCERPT_HISTORY)
.set(EXCERPT_HISTORY.ERVU_ID, ervuId)
.set(EXCERPT_HISTORY.PRN_OID, prnOid)
.set(EXCERPT_HISTORY.URL, url)
.set(EXCERPT_HISTORY.STATUS, status)
.execute();
}
public void update(Long id, String url, String status) {
dslContext.update(EXCERPT_HISTORY)
.set(EXCERPT_HISTORY.URL, url)
.set(EXCERPT_HISTORY.STATUS, status)
.set(EXCERPT_HISTORY.DATETIME, Timestamp.valueOf(LocalDateTime.now()))
.where(EXCERPT_HISTORY.ID.eq(id))
.execute();
}
public void delete(String ervuId, String prnOid) {
dslContext.deleteFrom(EXCERPT_HISTORY)
.where(EXCERPT_HISTORY.ERVU_ID.eq(ervuId)
.and(EXCERPT_HISTORY.PRN_OID.eq(prnOid)))
.execute();
}
public void deleteOlderThan(long readyFileHours, long pendingFileHours) {
dslContext.deleteFrom(EXCERPT_HISTORY)
.where(EXCERPT_HISTORY.STATUS.eq(ExcerptStatus.READY.name())
.and(EXCERPT_HISTORY.DATETIME.le(
Timestamp.valueOf(LocalDateTime.now().minus(readyFileHours, ChronoUnit.HOURS)))))
.or(EXCERPT_HISTORY.STATUS.in(ExcerptStatus.PENDING.name(), ExcerptStatus.NOT_FOUND.name())
.and(EXCERPT_HISTORY.DATETIME.le(
Timestamp.valueOf(LocalDateTime.now().minus(pendingFileHours, ChronoUnit.HOURS)))))
.execute();
}
}

View file

@ -42,8 +42,6 @@ public class ReplyingKafkaConfig {
private String groupId;
@Value("${ervu.kafka.reply.timeout:30}")
private long replyTimeout;
@Value("${ervu.kafka.excerpt.reply.topic}")
private String excerptReplyTopic;
@Value("${kafka.auth_sec_proto}")
private String securityProtocol;
@Value("${kafka.auth_sasl_module}")
@ -119,8 +117,7 @@ public class ReplyingKafkaConfig {
@Bean("ervuContainer")
public ConcurrentMessageListenerContainer<String, String> ervuContainer(
@Qualifier("ervuContainerFactory") ConcurrentKafkaListenerContainerFactory<String, String> factory) {
return factory.createContainer(orgReplyTopic, excerptReplyTopic,
journalReplyTopic, avReplyTopic);
return factory.createContainer(orgReplyTopic, journalReplyTopic, avReplyTopic);
}
@Bean("validateContainer")

View file

@ -1,96 +0,0 @@
package ru.micord.ervu.kafka.controller;
import java.time.ZonedDateTime;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import ervu.client.fileupload.WebDavClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.kafka.exception.ExcerptException;
import ru.micord.ervu.kafka.exception.ExcerptResponseException;
import ru.micord.ervu.kafka.model.Data;
import ru.micord.ervu.kafka.model.ExcerptResponse;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil;
import ru.micord.ervu.util.UrlUtils;
/**
* @author Eduard Tihomirov
*/
@RestController
public class ErvuKafkaController {
@Autowired
private ReplyingKafkaService<String, String> replyingKafkaService;
@Autowired
private AuditService auditService;
@Autowired
private WebDavClient webDavClient;
@Value("${ervu.kafka.excerpt.reply.topic}")
private String requestReplyTopic;
@Value("${ervu.kafka.excerpt.request.topic}")
private String requestTopic;
@Autowired
private ObjectMapper objectMapper;
@GetMapping(value = "/kafka/excerpt")
public ResponseEntity<Resource> getExcerptFile(HttpServletRequest request) {
String fileUrl = null;
String fileName = null;
long fileSize = 0;
try {
String clientTimeZone = request.getHeader("Client-Time-Zone");
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
Data data = new Data();
data.setErvuId(userIdsPair.getErvuId());
data.setPrnOid(userIdsPair.getEsiaUserId());
String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId())
.getOffset().getId();
data.setTimeZone(offset);
String kafkaResponse = replyingKafkaService.sendMessageAndGetReply(requestTopic,
requestReplyTopic, objectMapper.writeValueAsString(data));
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
if (!excerptResponse.getSuccess()) {
throw new ExcerptResponseException(
"Error with getting excerpt url " + excerptResponse.getMessage());
}
else if (excerptResponse.getData() == null || excerptResponse.getData().getFileUrl() == null
|| excerptResponse.getData().getFileUrl().isEmpty()) {
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
return ResponseEntity.noContent().build();
}
fileUrl = excerptResponse.getData().getFileUrl();
fileName = UrlUtils.extractFileNameFromUrl(excerptResponse.getData().getFileUrl());
ResponseEntity<Resource> responseEntity = webDavClient.webDavDownloadFile(fileUrl);
fileSize = responseEntity.getHeaders().getContentLength();
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.SUCCESS_STATUS_TYPE, fileUrl
);
return responseEntity;
}
catch (Exception e) {
auditService.processDownloadEvent(request, fileSize, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
throw new ExcerptException(e);
}
}
}

View file

@ -0,0 +1,94 @@
package ru.micord.ervu.kafka.listener;
import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper;
import ervu.enums.ExcerptStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Service;
import ru.micord.ervu.audit.constants.AuditConstants;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.dao.ExcerptHistoryDao;
import ru.micord.ervu.kafka.exception.ExcerptException;
import ru.micord.ervu.kafka.exception.ExcerptResponseException;
import ru.micord.ervu.kafka.model.ExcerptData;
import ru.micord.ervu.kafka.model.ExcerptResponse;
import ru.micord.ervu.util.UrlUtils;
/**
* @author gulnaz
*/
@Service
public class ExcerptListener {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ExcerptHistoryDao excerptHistoryDao;
@Autowired
private AuditService auditService;
@Value("${ervu.kafka.group.id}")
private String groupId;
@KafkaListener(
id = "#{excerptListener.getGroupId}",
topics = "${ervu.kafka.excerpt.reply.topic}",
containerFactory = "fileContainerFactory")
public void listen(String kafkaResponse) {
String fileUrl = null;
String fileName = null;
ExcerptData data = new ExcerptData();
try {
ExcerptResponse excerptResponse = objectMapper.readValue(kafkaResponse, ExcerptResponse.class);
if (!excerptResponse.getSuccess()) {
if (excerptResponse.getData() != null) {
data = excerptResponse.getData();
saveUrl(data.getOrgId(), data.getPrnOid(), fileUrl);
}
throw new ExcerptResponseException(
"Error with getting excerpt url " + excerptResponse.getMessage());
}
else if (excerptResponse.getData() == null || excerptResponse.getData().getFileUrl() == null
|| excerptResponse.getData().getFileUrl().isEmpty()) {
if (excerptResponse.getData() != null) {
data = excerptResponse.getData();
saveUrl(data.getOrgId(), data.getPrnOid(), fileUrl);
}
auditService.processDownloadEvent(data, 0, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
return;
}
data = excerptResponse.getData();
fileUrl = data.getFileUrl();
fileName = UrlUtils.extractFileNameFromUrl(fileUrl);
saveUrl(data.getOrgId(), data.getPrnOid(), fileUrl);
}
catch (Exception e) {
auditService.processDownloadEvent(data, 0, fileName, 1,
AuditConstants.FAILURE_STATUS_TYPE, fileUrl
);
throw new ExcerptException(e);
}
}
public String getGroupId() {
return groupId + "-" + UUID.randomUUID();
}
private void saveUrl(String ervuId, String prnOid, String fileUrl) {
String status = fileUrl == null ? ExcerptStatus.NOT_FOUND.name() : ExcerptStatus.READY.name();
excerptHistoryDao.get(ervuId, prnOid).ifPresentOrElse(
record -> excerptHistoryDao.update(record.getId(), fileUrl, status),
() -> excerptHistoryDao.insert(ervuId, prnOid, fileUrl, status)
);
}
}

View file

@ -16,6 +16,7 @@ public class Data implements Serializable {
private String ervuId;
private String prnOid;
private String timeZone;
private int year;
public String getErvuId() {
return ervuId;
@ -40,4 +41,12 @@ public class Data implements Serializable {
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
}

View file

@ -11,6 +11,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
public class ExcerptData implements Serializable {
private static final long serialVersionUID = 1L;
private String orgId;
private String prnOid;
private String timeZone;
private String fileUrl;
public String getOrgId() {
@ -21,6 +23,22 @@ public class ExcerptData implements Serializable {
this.orgId = orgId;
}
public String getPrnOid() {
return prnOid;
}
public void setPrnOid(String prnOid) {
this.prnOid = prnOid;
}
public String getTimeZone() {
return timeZone;
}
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
public String getFileUrl() {
return fileUrl;
}

View file

@ -0,0 +1,31 @@
package ru.micord.ervu.scheduler;
import net.javacrumbs.shedlock.core.SchedulerLock;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import ru.micord.ervu.dao.ExcerptHistoryDao;
/**
* @author gulnaz
*/
@Service
public class ExcerptCleanScheduler {
private final ExcerptHistoryDao excerptHistoryDao;
@Value("${excerpt.ready.lifetime.hours:24}")
private long readyFileLifetime;
@Value("${excerpt.pending.lifetime.hours:8}")
private long pendingFileLifetime;
public ExcerptCleanScheduler(ExcerptHistoryDao excerptHistoryDao) {
this.excerptHistoryDao = excerptHistoryDao;
}
@Scheduled(cron = "${excerpt.cleanup.cron:0 */30 * * * *}")
@SchedulerLock(name = "oldExcerptFilesCleaning")
public void cleanOld() {
excerptHistoryDao.deleteOlderThan(readyFileLifetime, pendingFileLifetime);
}
}

View file

@ -43,7 +43,12 @@ public final class DateUtils {
public static String getClientDateTimeWithZoneFromRequest(HttpServletRequest request) {
String clientTimeZone = request.getHeader("Client-Time-Zone");
return getClientDateTimeWithZone(clientTimeZone);
}
public static String getClientDateTimeWithZone(String clientTimeZone) {
ZoneId zoneId;
try {
zoneId = ZoneId.of(clientTimeZone);
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<changeSet id="001" author="gulnaz">
<comment>Add excerpt history table</comment>
<createTable schemaName="public" tableName="excerpt_history">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints nullable="false"/>
</column>
<column name="ervu_id" type="varchar(36)">
<constraints nullable="false"/>
</column>
<column name="prn_oid" type="varchar(10)">
<constraints nullable="false"/>
</column>
<column name="url" type="TEXT"/>
<column name="datetime" type="TIMESTAMP"/>
</createTable>
</changeSet>
</databaseChangeLog>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd">
<changeSet id="001" author="gulnaz">
<preConditions onFail="MARK_RAN" onFailMessage="Column status already exists">
<not>
<columnExists tableName="excerpt_history" columnName="status"/>
</not>
</preConditions>
<comment>Add status column to excerpt history table</comment>
<addColumn schemaName="public" tableName="excerpt_history">
<column name="status" type="varchar(20)"/>
</addColumn>
</changeSet>
<changeSet id="002" author="gulnaz">
<addDefaultValue columnName="datetime" tableName="excerpt_history" columnDataType="TIMESTAMP"
defaultValueComputed="CURRENT_TIMESTAMP"/>
<addNotNullConstraint tableName="excerpt_history" columnName="datetime"/>
</changeSet>
</databaseChangeLog>

View file

@ -11,5 +11,7 @@
<include file="2025-10-20-create-offset-column.xml" relativeToChangelogFile="true"/>
<include file="2025-10-24-add-org-allowed-table.xml" relativeToChangelogFile="true"/>
<include file="2025-11-05-rename-column.xml" relativeToChangelogFile="true"/>
<include file="2025-11-20_add-excerpt-history.xml" relativeToChangelogFile="true"/>
<include file="2025-11-26_add-status-column-to-excerpt-history.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View file

@ -70,7 +70,10 @@ WEBDAV_URLS=https://ervu-webdav.k8s.micord.ru,https://ervu-webdav1.k8s.micord.ru
WEBDAV_USERNAME=test
WEBDAV_PASSWORD=test
WEBDAV_BAD_SERVERS_CACHE_EXPIRE_SECONDS=120
WEBDAV_CLEANUP_CRON=0 0 0 * * *
WEBDAV_CLEANUP_CRON=0 */30 * * * *
WEBDAV_RETRY_DELAY=500
FILE_WEBDAV_LIFETIME_SECONDS=300
FILE_WEBDAV_LIFETIME_SECONDS=86400
FILE_WEBDAV_EXTENSIONS=csv,xlsx
EXCERPT_CLEANUP_CRON=0 */30 * * * *
EXCERPT_READY_LIFETIME_HOURS=24
EXCERPT_PENDING_LIFETIME_HOURS=8

View file

@ -108,11 +108,43 @@ paths:
application/json:
schema:
type: string
/kafka/excerpt:
/excerpt:
post:
summary: Отправка запроса на формирование выписки
operationId: requestExcerptUrl
description: Отправка запроса на формирование выписки по журналу взаимодействий с ЕРВУ
parameters:
- name: Client-Time-Zone
in: header
required: true
description: Таймзона клиента
schema:
type: string
requestBody:
content:
application/json:
schema:
required:
- year
type: integer
properties:
year:
description: Период формирования выписки
responses:
"200":
description: OK
/excerpt/download:
get:
summary: Получение выписки
operationId: getExcerptFile
description: Получение выписки по журналу взаимодействий с ЕРВУ
parameters:
- name: Client-Time-Zone
in: header
required: true
description: Таймзона клиента
schema:
type: string
responses:
"200":
description: OK
@ -127,6 +159,18 @@ paths:
type: object
"204":
description: No Content
/excerpt/status:
get:
summary: Получение статуса выписки
operationId: getStatus
description: Получение статуса выписки по журналу взаимодействий с ЕРВУ (NONE, PENDING, READY, NOT_FOUND)
responses:
"200":
description: OK
content:
application/json:
schema:
type: string
/rpc/filesentlog/bbaf33d7-0679-440b-a394-cb805ce80300/ru.micord.ervu.service.rpc.InMemoryStaticGridRpcService/loadData:
post:
summary: Получение данных по журналу взаимодействий

View file

@ -119,10 +119,24 @@
.webbpm.ervu_lkrp_ul ervu-download-file-button {
display: block;
}
.webbpm.ervu_lkrp_ul .fieldset text + ervu-download-file-button {
.webbpm.ervu_lkrp_ul .fieldset ervu-download-file-button,
.webbpm.ervu_lkrp_ul year-combo-box .selectize-input {
margin-top: var(--indent-mini);
}
.webbpm.ervu_lkrp_ul year-combo-box .selectize-dropdown-content {
padding: 5px 0;
}
.webbpm.ervu_lkrp_ul .message-clock {
background: url(../img/svg/clock-32x32.svg) no-repeat 0 0;
display: block;
font-size: var(--l-size-text-primary);
height: 32px;
margin: var(--indent-mini) 0;
padding-left: var(--indent-big);
}
.webbpm.ervu_lkrp_ul .btn,
.webbpm.ervu_lkrp_ul .fieldset .btn-main:not(.info):not(.link) .btn,
.webbpm.ervu_lkrp_ul .modal.show button-component.btn-main:not(.info):not(.link) .btn,

View file

@ -0,0 +1,45 @@
<div [id]="getObjectId()"
class="form-group">
<label [ngbTooltip]="tooltip | emptyIfNull"
[hidden]="!label" class="control-label">
<span>{{label}}<span *ngIf="isRequired()" class="alarm"> *</span></span>
</label>
<div class="component-float" [title]="getValueForTooltip()">
<select [name]="name"
class="form-control"
[ngModel]="getValueAsModel()"
#model="ngModel"
[ngStyle]="style"
[required]="isRequired()">
</select>
<div *ngIf="(model && model.touched && model.errors) || getCustomValidationMessages().size > 0 || warningMessages.length > 0" class="msg" [ngStyle]="errorTooltipStyle">
<div class="arrow"></div>
<div class="control-label">
<div class="msg-alert"
*ngFor="let warning of warningMessages">
{{warning}}
</div>
</div>
<div class="control-label">
<div class="msg-alert"
*ngFor="let customValidationMessage of getCustomValidationMessages()">
{{customValidationMessage}}
</div>
</div>
<div class="msg-alert control-label"
*ngIf="model.errors && model.errors['required']">
{{getErrorMessages()['required']}}
</div>
</div>
<div class="msg-versioning" *ngIf="getVersioningValidationMessage()" [ngStyle]="tooltipIfNotSatisfyStyle">
<div class="arrow"></div>
<div class="control-label">
<div class="msg-alert">
{{getVersioningValidationMessage()}}
</div>
</div>
</div>
</div>
</div>

View file

@ -1,8 +1,6 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
import {AbstractButton, MessagesService, NotNull, ObjectRef} from "@webbpm/base-package";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {InMemoryStaticGrid} from "../grid/InMemoryStaticGrid";
import {Subscription} from "rxjs";
/**
* @author: Eduard Tihomirov
@ -14,13 +12,9 @@ import {Subscription} from "rxjs";
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ErvuDownloadFileButton extends AbstractButton {
private httpClient: HttpClient;
private messageService: MessagesService;
private gridLoadedSubscription: Subscription;
@ObjectRef()
@NotNull()
public grid: InMemoryStaticGrid;
@NotNull()
public fileName: string;
@ -36,29 +30,10 @@ export class ErvuDownloadFileButton extends AbstractButton {
super.initialize();
this.httpClient = this.injector.get(HttpClient);
this.messageService = this.injector.get(MessagesService);
if (this.grid.isInitialized() && this.grid.getRowDataSize() > 0) {
this.setEnabled(true);
this.setVisible(true);
}
this.gridLoadedSubscription = this.grid.gridLoaded.subscribe(() => {
if (this.grid.getRowDataSize() > 0) {
this.setEnabled(true);
this.setVisible(true);
}
else {
this.setEnabled(false);
this.setVisible(false);
}
});
}
ngOnDestroy() {
super.ngOnDestroy();
this.gridLoadedSubscription.unsubscribe();
}
public doClickActions(): Promise<any> {
return this.httpClient.get('kafka/excerpt', {
return this.httpClient.get('excerpt/download', {
responseType: 'blob',
headers: new HttpHeaders({'Client-Time-Zone' : Intl.DateTimeFormat().resolvedOptions().timeZone}),
observe: 'response'

View file

@ -0,0 +1,67 @@
import {
AbstractButton, AnalyticalScope,
Behavior,
ComboBox, Dialog,
MessagesService,
NotNull,
ObjectRef
} from "@webbpm/base-package";
import {HttpClient, HttpHeaders} from "@angular/common/http";
@AnalyticalScope(AbstractButton)
export class ErvuFileRequestButton extends Behavior {
@NotNull()
@ObjectRef()
public year: ComboBox;
@NotNull()
@ObjectRef()
public beforeRequestDialog: Dialog;
@NotNull()
@ObjectRef()
public afterRequestDialog: Dialog;
private button: AbstractButton;
private httpClient: HttpClient;
private messageService: MessagesService;
private onClickFunction: Function;
private onYearValueChange: Function;
initialize() {
super.initialize();
this.button = this.getScript(AbstractButton);
this.httpClient = this.injector.get(HttpClient);
this.messageService = this.injector.get(MessagesService);
this.onClickFunction = () => {
if (!this.year.getValue()) {
this.messageService.error("Не выбран период");
return;
}
this.httpClient.post("excerpt", this.year.getValue(), {
headers: new HttpHeaders(
{'Client-Time-Zone': Intl.DateTimeFormat().resolvedOptions().timeZone})
}).toPromise()
.then(() => {
this.beforeRequestDialog.hide();
this.afterRequestDialog.show();
})
.catch(e => this.messageService.error(e));
};
this.onYearValueChange = () => this.button.setEnabled(this.year.getValue());
}
bindEvents() {
super.bindEvents();
this.button.addClickListener(this.onClickFunction);
this.year.addUserChangeValueListener(this.onYearValueChange);
}
unbindEvents() {
super.unbindEvents();
this.button.removeClickListener(this.onClickFunction);
this.year.removeUserChangeValueListener(this.onYearValueChange);
}
}

View file

@ -0,0 +1,76 @@
import {AbstractButton, AnalyticalScope, Behavior, NotNull, ObjectRef, Text} from "@webbpm/base-package";
import {Subscription} from "rxjs";
import {InMemoryStaticGrid} from "../grid/InMemoryStaticGrid";
import {OnDestroy} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import { ExcerptStatus } from "../../../generated/ervu/enums/ExcerptStatus";
@AnalyticalScope(AbstractButton)
export class ErvuFileRequestDialogButton extends Behavior implements OnDestroy {
@ObjectRef()
@NotNull()
public grid: InMemoryStaticGrid;
@ObjectRef()
@NotNull()
public text: Text;
@ObjectRef()
@NotNull()
public downloadButton: AbstractButton;
@ObjectRef()
@NotNull()
public notFoundText: Text;
@NotNull()
public requestSentMsg: string;
private button: AbstractButton;
private httpClient: HttpClient;
private gridLoadedSubscription: Subscription;
private fileStatus: string = ExcerptStatus.NONE;
initialize() {
super.initialize();
this.button = this.getScript(AbstractButton);
this.httpClient = this.injector.get(HttpClient);
this.gridLoadedSubscription = this.grid.gridLoaded.subscribe(() => {
this.button.setEnabled(this.grid.getRowDataSize() > 0
&& this.fileStatus !== ExcerptStatus.PENDING);
this.button.setVisible(this.grid.getRowDataSize() > 0);
});
}
start() {
super.start();
this.getFileStatus()
.then(status => {
this.fileStatus = status;
if (this.grid.isInitialized() && this.grid.getRowDataSize() > 0) {
this.button.setEnabled(status !== ExcerptStatus.PENDING);
this.button.setVisible(true);
}
if (status === ExcerptStatus.PENDING) {
this.button.setTooltip(this.requestSentMsg);
this.text.setValue(this.requestSentMsg);
}
this.downloadButton.setEnabled(status === ExcerptStatus.READY);
this.downloadButton.setVisible(status === ExcerptStatus.READY);
this.notFoundText.setVisible(status === ExcerptStatus.NOT_FOUND);
});
}
private getFileStatus(): Promise<string> {
return this.httpClient.get('excerpt/status', {observe: 'response'}).toPromise()
.then(res => res.body.toString());
}
ngOnDestroy() {
this.gridLoadedSubscription.unsubscribe();
}
}

View file

@ -0,0 +1,36 @@
import {ComboBox, ComboBoxModel} from "@webbpm/base-package";
import {ChangeDetectionStrategy, Component} from "@angular/core";
@Component({
moduleId: module.id,
selector: 'year-combo-box',
templateUrl: './../../../../../src/resources/template/ervu/component/combobox/ComboBox.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class YearComboBox extends ComboBox {
private START_YEAR: number = 2025;
initialize(): Promise<void> {
return this.loadData();
}
loadData(): Promise<void> {
let currentYear = new Date().getFullYear();
let startYear = this.START_YEAR;
let years: ComboBoxModel[] = [];
while (startYear <= currentYear) {
let value = startYear++;
let model: ComboBoxModel = new ComboBoxModel();
model.id = value;
model.label = `${value}`;
model.dropDownLabel = `${value}`;
model.valid = true;
model.hidden = false;
years.push(model);
}
return Promise.resolve()
.then(() => this.setData(years));
}
}

View file

@ -30,6 +30,7 @@ import {
ErvuFileUploadWithAdditionalFiles
} from "../../ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles";
import {ValidateFileService} from "../../ervu/service/ValidateFileService";
import {YearComboBox} from "../../ervu/component/combobox/YearComboBox";
registerLocaleData(localeRu);
export const DIRECTIVES = [
@ -42,6 +43,7 @@ export const DIRECTIVES = [
forwardRef(() => ErvuFileUploadWithAdditionalFiles),
forwardRef(() => ErvuDownloadFileButton),
forwardRef(() => InMemoryStaticGrid),
forwardRef(() => YearComboBox)
];
export function checkAuthentication(authService: AuthenticationService): () => Promise<any> {

View file

@ -2037,13 +2037,6 @@
<scripts id="73f52c22-5182-4860-8ee6-b9ba164ed460"/>
<scripts id="2129eba5-aac3-41d1-ba50-0a2f4b5f0a32"/>
<scripts id="865a51e1-80f5-4064-a7d2-2b251b33bdec"/>
<children id="358d1918-2e9b-4123-a8b5-8189a1789291">
<prototypeId>d7d54cfb-26b5-4dba-b56f-b6247183c24d</prototypeId>
<componentRootId>358d1918-2e9b-4123-a8b5-8189a1789291</componentRootId>
<name>Горизонтальный контейнер</name>
<container>true</container>
<removed>true</removed>
</children>
<children id="097a2141-13d0-4b15-ac5f-5c310b3753f7">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>097a2141-13d0-4b15-ac5f-5c310b3753f7</componentRootId>
@ -2066,7 +2059,7 @@
<entry>
<key>initialValue</key>
<value>
<simple>"Запросите выписку, чтобы посмотреть журнал взаимодействий за весь период"</simple>
<simple>"Запросите выписку о переданных сведениях по сотрудникам в Военные комиссариаты"</simple>
</value>
</entry>
<entry>
@ -2084,10 +2077,90 @@
<enabled>false</enabled>
</scripts>
</children>
<children id="a3c68e2f-424c-4908-9983-b5e3adf8805b">
<prototypeId>d7d54cfb-26b5-4dba-b56f-b6247183c24d</prototypeId>
<componentRootId>a3c68e2f-424c-4908-9983-b5e3adf8805b</componentRootId>
<name>Hbox</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="b6068710-0f31-48ec-8e03-c0c1480a40c0"/>
<scripts id="fe04d7fb-6c5b-46c4-b723-667732d81f4f"/>
<scripts id="5c566210-2a60-4048-a2d1-84c7dd023248"/>
<scripts id="3171b2e1-b4af-4335-95fa-1b2592604b84"/>
<children id="cddb82f9-6e93-4fde-a30c-60bbced3b380">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>cddb82f9-6e93-4fde-a30c-60bbced3b380</componentRootId>
<name>Кнопка диалога запроса выписки</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
<entry>
<key>caption</key>
<value>
<simple>"Запросить выписку"</simple>
</value>
</entry>
<entry>
<key>disabled</key>
<value>
<simple>true</simple>
</value>
</entry>
<entry>
<key>visible</key>
<value>
<simple>false</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="d99b7472-aae1-4f14-b2f7-065423a9a1ab">
<classRef type="TS">
<className>ErvuFileRequestDialogButton</className>
<packageName>ervu.component.button</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>downloadButton</key>
<value>
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>grid</key>
<value>
<simple>{"objectId":"bbaf33d7-0679-440b-a394-cb805ce80300","packageName":"ervu.component.grid","className":"InMemoryStaticGrid","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>notFoundText</key>
<value>
<simple>{"objectId":"06657c0b-4e2e-402b-a40b-099d15da551d","packageName":"component","className":"Text","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>requestSentMsg</key>
<value>
<simple>"Дождитесь формирования выписки"</simple>
</value>
</entry>
<entry>
<key>text</key>
<value>
<simple>{"objectId":"097a2141-13d0-4b15-ac5f-5c310b3753f7","packageName":"component","className":"Text","type":"TS"}</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="2528ea8a-3e3f-4968-a973-8e9ac1ca4326">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>2528ea8a-3e3f-4968-a973-8e9ac1ca4326</componentRootId>
<name>Кнопка</name>
<name>Скачать</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
@ -2105,7 +2178,7 @@
<entry>
<key>caption</key>
<value>
<simple>"Запросить выписку"</simple>
<simple>"Скачать выписку"</simple>
</value>
</entry>
<entry>
@ -2120,12 +2193,6 @@
<simple>"Выписка.xlsx"</simple>
</value>
</entry>
<entry>
<key>grid</key>
<value>
<simple>{"objectId":"bbaf33d7-0679-440b-a394-cb805ce80300","packageName":"ervu.component.grid","className":"InMemoryStaticGrid","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>noFileMessage</key>
<value>
@ -2141,10 +2208,358 @@
</properties>
</scripts>
</children>
<children id="78640b5d-37d1-4940-aafc-fc7a38b907cf">
<children id="06657c0b-4e2e-402b-a40b-099d15da551d">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>06657c0b-4e2e-402b-a40b-099d15da551d</componentRootId>
<name>Выписка не найдена</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Выписка по предыдущему запросу не найдена"</simple>
</value>
</entry>
<entry>
<key>style</key>
<value>
<complex>
<entry>
<key>margin</key>
<value>
<simple>"16px"</simple>
</value>
</entry>
<entry>
<key>padding</key>
<value>
<simple>"12px 0 0"</simple>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>visible</key>
<value>
<simple>false</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
<scripts id="a6ccccd9-354c-4725-9d34-c716cf626048"/>
<scripts id="d38c1af5-2bfe-41cd-ab0f-67040f498127"/>
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
</children>
<children id="cb657c69-1023-4462-ada6-03c1a9fbcb8e">
<prototypeId>86f297f1-ab3d-40e0-ac2f-89cc944b7f0a</prototypeId>
<componentRootId>cb657c69-1023-4462-ada6-03c1a9fbcb8e</componentRootId>
<name>Dialog: запросить выписку</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c"/>
<scripts id="48d405ee-5991-4027-bfee-113a895bf8f8"/>
<scripts id="b463917a-16fc-42db-9c92-9c1027e9232e"/>
<scripts id="24a13b10-fec4-420e-aa2e-5af0ec41326a"/>
<scripts id="5f57bb31-85b0-4692-9f1f-d6369dea6e95"/>
<children id="55a6bd55-8a44-4b4f-8070-9dc36d2bcf3a">
<prototypeId>b310f98a-69c6-4e7b-8cdb-f1ab9f9c0d94</prototypeId>
<componentRootId>55a6bd55-8a44-4b4f-8070-9dc36d2bcf3a</componentRootId>
<name>Выберите период</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="23992f0e-94ed-4fb4-b4d1-dc6ad7f13227">
<enabled>false</enabled>
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="efb0fec7-9951-4b36-bbda-fa17aa002d74">
<enabled>false</enabled>
<expanded>false</expanded>
</scripts>
<scripts id="4d028ea6-e4a3-4acf-bd60-de7aa1a78f71">
<enabled>false</enabled>
<expanded>false</expanded>
</scripts>
<scripts id="9f543b36-92e3-4a63-b8db-a4d7e852113e">
<enabled>false</enabled>
<expanded>false</expanded>
</scripts>
<scripts id="47f307b6-79a7-4c9a-96d6-6ee423565f02">
<enabled>false</enabled>
<expanded>false</expanded>
</scripts>
<scripts id="ec1e9370-303a-4a7e-948f-27ef7687cd03">
<enabled>false</enabled>
<expanded>false</expanded>
</scripts>
<scripts id="8dc2d4a1-3734-450c-8136-6bb76ac67f19">
<classRef type="TS">
<className>YearComboBox</className>
<packageName>ervu.component.combobox</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>label</key>
<value>
<simple>"Выберите период"</simple>
</value>
</entry>
<entry>
<key>visible</key>
<value>
<simple>true</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="6a2c076b-7a39-4afc-bb43-49fff00b21e3">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>6a2c076b-7a39-4afc-bb43-49fff00b21e3</componentRootId>
<name>Запросить выписку</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
<entry>
<key>caption</key>
<value>
<simple>"Запросить выписку"</simple>
</value>
</entry>
<entry>
<key>disabled</key>
<value>
<simple>true</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="c92fdf96-a832-4f5d-ad06-5873382fc045">
<classRef type="TS">
<className>ErvuFileRequestButton</className>
<packageName>ervu.component.button</packageName>
</classRef>
<enabled>true</enabled>
<expanded>true</expanded>
<properties>
<entry>
<key>afterRequestDialog</key>
<value>
<simple>{"objectId":"6ed640f7-eab1-4dcc-99c0-c9a49c85292f","packageName":"component","className":"Dialog","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>beforeRequestDialog</key>
<value>
<simple>{"objectId":"cb657c69-1023-4462-ada6-03c1a9fbcb8e","packageName":"component","className":"Dialog","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>year</key>
<value>
<simple>{"objectId":"55a6bd55-8a44-4b4f-8070-9dc36d2bcf3a","packageName":"component.field","className":"ComboBox","type":"TS"}</simple>
</value>
</entry>
</properties>
</scripts>
</children>
</children>
<children id="6ed640f7-eab1-4dcc-99c0-c9a49c85292f">
<prototypeId>86f297f1-ab3d-40e0-ac2f-89cc944b7f0a</prototypeId>
<componentRootId>6ed640f7-eab1-4dcc-99c0-c9a49c85292f</componentRootId>
<name>Dialog: запрос отправлен</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c"/>
<scripts id="48d405ee-5991-4027-bfee-113a895bf8f8"/>
<scripts id="b463917a-16fc-42db-9c92-9c1027e9232e"/>
<scripts id="24a13b10-fec4-420e-aa2e-5af0ec41326a"/>
<scripts id="5f57bb31-85b0-4692-9f1f-d6369dea6e95"/>
<children id="ead2d930-7157-4d98-b778-67f6a378bcd7">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>ead2d930-7157-4d98-b778-67f6a378bcd7</componentRootId>
<name>Заголовок</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="278ba196-69c3-4f3f-ac32-6c217a7129a7" removed="false">
<value>
<simple>"title"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Запрос отправлен"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
<scripts id="a6ccccd9-354c-4725-9d34-c716cf626048"/>
<scripts id="d38c1af5-2bfe-41cd-ab0f-67040f498127"/>
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
</children>
<children id="882c5346-4c05-43eb-9675-8fc860565429">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>882c5346-4c05-43eb-9675-8fc860565429</componentRootId>
<name>Сообщение</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Выписка будет сформирована Государственной информационной системой \"Единый реестр воинского учета\" (ГИС ЕРВУ) и направлена в ваш личный кабинет Реестра повесток"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
<scripts id="a6ccccd9-354c-4725-9d34-c716cf626048"/>
<scripts id="d38c1af5-2bfe-41cd-ab0f-67040f498127"/>
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
</children>
<children id="b84e5d1f-ad34-44b9-aca4-29b2083aea37">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>b84e5d1f-ad34-44b9-aca4-29b2083aea37</componentRootId>
<name>Время</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="5b32e81b-8c6b-47e6-9779-be0b36003695" removed="false">
<value>
<simple>"message-clock"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Услуга будет оказана в течение дня"</simple>
</value>
</entry>
</properties>
</scripts>
<scripts id="737b67e2-295f-4356-a1e1-9419344d8c85"/>
<scripts id="a6ccccd9-354c-4725-9d34-c716cf626048"/>
<scripts id="d38c1af5-2bfe-41cd-ab0f-67040f498127"/>
<scripts id="f203f156-be32-4131-9c86-4d6bac6d5d56">
<enabled>false</enabled>
</scripts>
</children>
<children id="b8655a1c-b30f-426c-bbd4-5799281f0752">
<prototypeId>c8dfe691-a84a-48da-b79e-6298d90db71d</prototypeId>
<componentRootId>b8655a1c-b30f-426c-bbd4-5799281f0752</componentRootId>
<name>Navigation: В личный кабинет</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f">
<properties>
<entry>
<key>caption</key>
<value>
<simple>"В личный кабинет"</simple>
</value>
</entry>
<entry>
<key>navigateTo</key>
<value>
<simple>"/"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
</children>
</children>
<children id="6ed640f7-eab1-4dcc-99c0-c9a49c85292f">
<prototypeId>86f297f1-ab3d-40e0-ac2f-89cc944b7f0a</prototypeId>
<componentRootId>6ed640f7-eab1-4dcc-99c0-c9a49c85292f</componentRootId>
<name>Dialog: запрос отправлен</name>
<container>true</container>
<removed>true</removed>
</children>
<children id="cddb82f9-6e93-4fde-a30c-60bbced3b380">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>cddb82f9-6e93-4fde-a30c-60bbced3b380</componentRootId>
<name>Кнопка диалога запроса выписки</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="cb657c69-1023-4462-ada6-03c1a9fbcb8e">
<prototypeId>86f297f1-ab3d-40e0-ac2f-89cc944b7f0a</prototypeId>
<componentRootId>cb657c69-1023-4462-ada6-03c1a9fbcb8e</componentRootId>
<name>Dialog</name>
<container>true</container>
<removed>true</removed>
</children>
<children id="2528ea8a-3e3f-4968-a973-8e9ac1ca4326">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>2528ea8a-3e3f-4968-a973-8e9ac1ca4326</componentRootId>
<name>Скачать</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="f4ae195f-3afe-473e-ba9e-72c966b55b25">
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
<componentRootId>78640b5d-37d1-4940-aafc-fc7a38b907cf</componentRootId>
<name>Action Controller</name>
<componentRootId>f4ae195f-3afe-473e-ba9e-72c966b55b25</componentRootId>
<name>AС: видимость текста</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="37dff5c8-1599-4984-b107-c44a87b6da2e">
@ -2152,7 +2567,9 @@
<entry>
<key>elseActions</key>
<value>
<item id="80889965-e277-41cc-bbc5-ea7948ad8b60" removed="false">
<item id="0d72a8ee-d2f3-4e93-9a4a-2b91c70b63d6" removed="true"/>
<item id="46eb6480-4c66-473e-926d-622fc73b25a7" removed="true"/>
<item id="6124572f-0dcd-48f1-80b9-08b84357aaf4" removed="false">
<value>
<complex>
<entry>
@ -2192,13 +2609,13 @@
<entry>
<key>eventRefs</key>
<value>
<item id="6f361a45-7ce6-4ca5-b927-ed4db7b2e39c" removed="false">
<item id="a6dcb50f-3d74-406c-b44d-5df6f199ab70" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
<simple>{"objectId":"cddb82f9-6e93-4fde-a30c-60bbced3b380","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
@ -2219,7 +2636,7 @@
<entry>
<key>conditions</key>
<value>
<item id="558f60e6-3e60-4fc8-b790-dc7ebd17b4d4" removed="false">
<item id="890c0449-3274-4408-abb9-ec01e8a64ad8" removed="false">
<value>
<complex>
<entry>
@ -2243,7 +2660,7 @@
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
<simple>{"objectId":"cddb82f9-6e93-4fde-a30c-60bbced3b380","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
@ -2301,7 +2718,9 @@
<entry>
<key>thenActions</key>
<value>
<item id="db6a9474-f16f-4015-8103-655173944b7e" removed="false">
<item id="80b3e9b1-d416-49c0-ba90-28224e564510" removed="true"/>
<item id="bafd5b4b-0600-4bb8-a05f-49e7e7d8ff83" removed="true"/>
<item id="50e53dfc-b185-4b63-b1a2-cbac45d6445a" removed="false">
<value>
<complex>
<entry>
@ -2341,6 +2760,330 @@
</properties>
</scripts>
</children>
<children id="55a6bd55-8a44-4b4f-8070-9dc36d2bcf3a">
<prototypeId>b310f98a-69c6-4e7b-8cdb-f1ab9f9c0d94</prototypeId>
<componentRootId>55a6bd55-8a44-4b4f-8070-9dc36d2bcf3a</componentRootId>
<name>Выберите период</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="6a2c076b-7a39-4afc-bb43-49fff00b21e3">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>6a2c076b-7a39-4afc-bb43-49fff00b21e3</componentRootId>
<name>Запросить выписку</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="7fbfc96c-0c1a-46e0-938b-ec45e75341bd">
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
<componentRootId>7fbfc96c-0c1a-46e0-938b-ec45e75341bd</componentRootId>
<name>AC: показать диалог для запроса</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="37dff5c8-1599-4984-b107-c44a87b6da2e">
<properties>
<entry>
<key>eventRefs</key>
<value>
<item id="1ebba0cc-903d-4d4a-ace0-b883a8d72fef" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"cddb82f9-6e93-4fde-a30c-60bbced3b380","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>propertyName</key>
<value>
<simple>"clickEvent"</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
<entry>
<key>ifCondition</key>
<value>
<complex>
<entry>
<key>logicalOperation</key>
<value>
<simple>null</simple>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>thenActions</key>
<value>
<item id="ec91ecee-66a4-4c63-9005-d0b37d262ee7" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"cb657c69-1023-4462-ada6-03c1a9fbcb8e","packageName":"component","className":"Dialog","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"show"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<simple>null</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="8825f9d1-53b1-438f-9b32-4850eba28bd7">
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
<componentRootId>8825f9d1-53b1-438f-9b32-4850eba28bd7</componentRootId>
<name>AC: доступность кнопки</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="37dff5c8-1599-4984-b107-c44a87b6da2e">
<properties>
<entry>
<key>eventRefs</key>
<value>
<item id="447ef47f-e5ac-45a1-ab6c-9975db866093" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"6ed640f7-eab1-4dcc-99c0-c9a49c85292f","packageName":"component","className":"Dialog","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>propertyName</key>
<value>
<simple>"hideDialogEvent"</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
<entry>
<key>ifCondition</key>
<value>
<complex>
<entry>
<key>logicalOperation</key>
<value>
<simple>null</simple>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>thenActions</key>
<value>
<item id="fa958867-a8c8-4129-8821-705da43a225e" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"cddb82f9-6e93-4fde-a30c-60bbced3b380","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"setEnabled"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<complex>
<entry>
<key>staticValue</key>
<value>
<implRef type="TS">
<className>boolean</className>
<packageName></packageName>
</implRef>
<simple>false</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="15c453fa-2b8f-497b-bdb3-d863af9ac436" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"cddb82f9-6e93-4fde-a30c-60bbced3b380","packageName":"component.button","className":"Button","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"setTooltip"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<complex>
<entry>
<key>staticValue</key>
<value>
<implRef type="TS">
<className>string</className>
<packageName></packageName>
</implRef>
<simple>"Дождитесь формирования выписки"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="ac6fbec0-1ee0-4b2c-b934-00327039e72a" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"097a2141-13d0-4b15-ac5f-5c310b3753f7","packageName":"component","className":"Text","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"setValue"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<complex>
<entry>
<key>staticValue</key>
<value>
<implRef type="TS">
<className>string</className>
<packageName></packageName>
</implRef>
<simple>"Дождитесь формирования выписки"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="de8f7972-0364-4fa8-b94f-c9d6bc9d9107" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"setEnabled"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<complex>
<entry>
<key>staticValue</key>
<value>
<implRef type="TS">
<className>boolean</className>
<packageName></packageName>
</implRef>
<simple>false</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="484cc7fa-46d2-4f98-95b8-992d64649e79" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"2528ea8a-3e3f-4968-a973-8e9ac1ca4326","packageName":"ervu.component.button","className":"ErvuDownloadFileButton","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"setVisible"</simple>
</value>
</entry>
<entry>
<key>value</key>
<value>
<complex>
<entry>
<key>staticValue</key>
<value>
<implRef type="TS">
<className>boolean</className>
<packageName></packageName>
</implRef>
<simple>false</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="0480ebf1-7636-46f4-9b68-7a30c08b7d4d">
<prototypeId>f4ca0e36-67d2-4882-9032-96718d6892da</prototypeId>
<componentRootId>0480ebf1-7636-46f4-9b68-7a30c08b7d4d</componentRootId>
<name>Download button</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="f4d7dfb7-67e6-41bd-bef6-ae9751ce2088">
<prototypeId>3057d447-6d17-48a8-b096-b14ea88d17e8</prototypeId>
<componentRootId>f4d7dfb7-67e6-41bd-bef6-ae9751ce2088</componentRootId>
@ -2348,13 +3091,6 @@
<container>false</container>
<removed>true</removed>
</children>
<children id="484748b4-a03d-4254-a7f3-fec2402e7866">
<prototypeId>d7d54cfb-26b5-4dba-b56f-b6247183c24d</prototypeId>
<componentRootId>484748b4-a03d-4254-a7f3-fec2402e7866</componentRootId>
<name>Горизонтальный контейнер</name>
<container>true</container>
<removed>true</removed>
</children>
<children id="87481ea6-30aa-4002-81d0-421e066298ff">
<prototypeId>e32ae1f5-5b14-45f1-abb6-f52c34b3b570</prototypeId>
<componentRootId>87481ea6-30aa-4002-81d0-421e066298ff</componentRootId>
@ -2384,5 +3120,12 @@
<removed>true</removed>
</children>
</children>
<children id="9078f42f-c838-4d38-9e46-4419d12eb80d">
<prototypeId>a4d442cf-72a8-431e-815a-490959083787</prototypeId>
<componentRootId>9078f42f-c838-4d38-9e46-4419d12eb80d</componentRootId>
<name>SecurityFeatures</name>
<container>true</container>
<removed>true</removed>
</children>
</rootObjects>
</xmlPage>