SUPPORT-9339: add sign verify

This commit is contained in:
Eduard Tihomirov 2025-08-28 14:15:18 +03:00
parent 838e1f8358
commit af52f7df8d
18 changed files with 1662 additions and 270 deletions

View file

@ -202,6 +202,10 @@
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.parent.artifactId}</finalName>

View file

@ -13,10 +13,7 @@ import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -89,12 +86,22 @@ public class WebDavClient {
}
@Retryable(value = {IOException.class}, backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}"))
public String uploadFile(MultipartFile multipartFile) {
String fileName = getNewFilename(multipartFile.getOriginalFilename());
public Map<String, String> uploadFiles(Map<String, MultipartFile> files) {
String fileCatalog = UUID.randomUUID() + "/";
Sardine sardine = initClient(username, password);
try {
return putAndGetUrl(multipartFile.getBytes(), fileName, sardine);
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, MultipartFile> entry : files.entrySet()) {
String key = entry.getKey();
MultipartFile file = entry.getValue();
if (file != null) {
String fileName = fileCatalog + file.getOriginalFilename();
result.put(key, putAndGetUrl(file.getBytes(), fileName, sardine));
}
}
return result;
}
catch (IOException e) {
throw new WebDavException("Failed to put file into WebDAV", e);
@ -109,12 +116,6 @@ public class WebDavClient {
}
}
private String getNewFilename(String filename) {
int lastIndexOf = filename.lastIndexOf(".");
String fileExtension = lastIndexOf == -1 ? "" : filename.substring(lastIndexOf);
return UUID.randomUUID() + fileExtension;
}
public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client) throws IOException {
if (badServersCache.size() == urls.length) {
return null;

View file

@ -26,13 +26,15 @@ public class EmployeeInfoFileUploadController {
@RequestMapping(value = "/employee/document", method = RequestMethod.POST)
public ResponseEntity<?> saveEmployeeInformationFile(
@RequestParam("file") MultipartFile multipartFile,
@RequestParam("signFile") MultipartFile signFile,
@RequestParam(value = "mchdFile", required = false) MultipartFile mchdFile,
@RequestHeader("X-Employee-Info-File-Form-Type") String formType,
@RequestHeader("Client-Time-Zone") String clientTimeZone) {
String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId())
.getOffset().getId();
if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType, offset)) {
if (this.fileUploadService.saveEmployeeInformationFiles(multipartFile, signFile, mchdFile, formType, offset)) {
return ResponseEntity.ok("File successfully uploaded.");
}

View file

@ -3,5 +3,5 @@ package ervu.model.fileupload;
/**
* @author r.latypov
*/
public record DownloadResponse(UploadOrgInfo orgInfo, FileInfo fileInfo) {
public record DownloadResponse(UploadOrgInfo orgInfo, FileInfo[] filesInfo) {
}

View file

@ -1,5 +1,6 @@
package ervu.model.fileupload;
import java.util.Arrays;
import java.util.Objects;
/**
@ -7,19 +8,19 @@ import java.util.Objects;
*/
public class EmployeeInfoKafkaMessage {
private final UploadOrgInfo orgInfo;
private final FileInfo fileInfo;
private final FileInfo[] filesInfo;
public EmployeeInfoKafkaMessage(UploadOrgInfo orgInfo, FileInfo fileInfo) {
public EmployeeInfoKafkaMessage(UploadOrgInfo orgInfo, FileInfo[] filesInfo) {
this.orgInfo = orgInfo;
this.fileInfo = fileInfo;
this.filesInfo = filesInfo;
}
public UploadOrgInfo getOrgInfo() {
return orgInfo;
}
public FileInfo getFileInfo() {
return fileInfo;
public FileInfo[] getFilesInfo() {
return filesInfo;
}
@Override
@ -27,19 +28,21 @@ public class EmployeeInfoKafkaMessage {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmployeeInfoKafkaMessage that = (EmployeeInfoKafkaMessage) o;
return Objects.equals(orgInfo, that.orgInfo) && Objects.equals(fileInfo, that.fileInfo);
return Objects.equals(orgInfo, that.orgInfo) && Arrays.equals(filesInfo, that.filesInfo);
}
@Override
public int hashCode() {
return Objects.hash(orgInfo, fileInfo);
int result = Objects.hash(orgInfo);
result = 31 * result + Arrays.hashCode(filesInfo);
return result;
}
@Override
public String toString() {
return "KafkaMessage{" +
"uploadOrgInfo=" + orgInfo +
", fileInfo=" + fileInfo +
", fileInfo=" + Arrays.toString(filesInfo) +
'}';
}
}

View file

@ -69,6 +69,14 @@ public class FileInfo {
return fileStatus;
}
public void setFileStatus(FileStatus fileStatus) {
this.fileStatus = fileStatus;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View file

@ -1,21 +1,29 @@
package ervu.service.fileupload;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Locale;
import java.util.UUID;
import java.util.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import ervu.client.fileupload.WebDavClient;
import ervu.model.fileupload.DownloadResponse;
import ervu.model.fileupload.EmployeeInfoFileFormType;
import ervu.model.fileupload.EmployeeInfoKafkaMessage;
import ervu.model.fileupload.FileInfo;
import ervu.model.fileupload.FileStatus;
import ervu.model.fileupload.*;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
@ -32,8 +40,12 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import ru.micord.ervu.audit.service.AuditService;
import ru.micord.ervu.exception.JsonParsingException;
import ru.micord.ervu.security.esia.config.EsiaConfig;
import ru.micord.ervu.security.esia.exception.EsiaException;
import ru.micord.ervu.security.esia.model.EmployeeModel;
import ru.micord.ervu.security.esia.model.MchdInfoModel;
import ru.micord.ervu.security.esia.model.PersonModel;
import ru.micord.ervu.security.esia.model.VerifyDocumentSignResponse;
import ru.micord.ervu.security.esia.service.UlDataService;
import ru.micord.ervu.security.esia.EsiaAuthInfoStore;
import ru.micord.ervu.security.webbpm.jwt.UserIdsPair;
@ -59,6 +71,8 @@ public class EmployeeInfoFileUploadService {
private final InteractionService interactionService;
private final UlDataService ulDataService;
private final AuditService auditService;
private final ObjectMapper objectMapper;
private final EsiaConfig esiaConfig;
@Value("${av.kafka.message.topic.name}")
private String kafkaTopicName;
@ -69,24 +83,35 @@ public class EmployeeInfoFileUploadService {
@Qualifier("avTemplate") KafkaTemplate<String, String> kafkaTemplate,
InteractionService interactionService,
UlDataService ulDataService,
AuditService auditService) {
AuditService auditService,
ObjectMapper objectMapper,
EsiaConfig esiaConfig) {
this.webDavClient = webDavClient;
this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService;
this.kafkaTemplate = kafkaTemplate;
this.interactionService = interactionService;
this.ulDataService = ulDataService;
this.auditService = auditService;
this.objectMapper = objectMapper;
this.esiaConfig = esiaConfig;
}
public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType,
public boolean saveEmployeeInformationFiles(MultipartFile multipartFile,
MultipartFile signFile, MultipartFile mchdFile, String formType,
String offset) {
UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair();
LocalDateTime now = LocalDateTime.now();
String departureDateTime = DateUtils.convertToString(now);
if (userIdsPair == null || !isValid(multipartFile)) {
if (userIdsPair == null || !isValidCsvWithSig(multipartFile, signFile)) {
return false;
}
String fileId = UUID.randomUUID().toString();
String signFileId = UUID.randomUUID().toString();
String fileName = multipartFile.getOriginalFilename();
long fileSize = multipartFile.getSize();
String signFileName = signFile.getOriginalFilename();
long signFileSize = signFile.getSize();
EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(
formType);
String esiaUserId = userIdsPair.getEsiaUserId();
@ -94,11 +119,52 @@ public class EmployeeInfoFileUploadService {
String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId);
EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken);
PersonModel personModel = employeeModel.getPerson();
String fileUploadUrl = this.webDavClient.uploadFile(multipartFile);
EmployeeModel chiefModel = ulDataService.getChiefEmployeeModel(accessToken);
VerifyDocumentSignResponse verifyDocumentSignResponse = validateSign(multipartFile, signFile);
FileStatus fileStatus = new FileStatus();
fileStatus.setStatus(fileUploadUrl == null ? "Невозможно проверить файл ЛК РП" : "Загрузка");
LocalDateTime now = LocalDateTime.now();
String fio = convertToFio(personModel.getFirstName(), personModel.getMiddleName(),
personModel.getLastName()
);
UploadOrgInfo uploadOrgInfo = employeeInfoKafkaMessageService.getOrgInfo(accessToken, ervuId,
esiaUserId, personModel
);
FileInfo fileInfo = employeeInfoKafkaMessageService.getFileInfo(fileId, null, fileName,
employeeInfoFileFormType, departureDateTime, offset, null, fileSize
);
FileInfo signFileInfo = employeeInfoKafkaMessageService.getFileInfo(signFileId, null,
signFileName, employeeInfoFileFormType, departureDateTime, offset, null, signFileSize
);
if (verifyDocumentSignResponse.getSignVerifyResult() != null) {
fileStatus.setStatus("Некорректная ЭП");
fileInfo.setFileStatus(fileStatus);
signFileInfo.setFileStatus(fileStatus);
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
fio, ervuId
);
auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {
fileInfo, signFileInfo
});
//Заменить на свою ошибку
throw new RuntimeException(verifyDocumentSignResponse.getSignVerifyResult());
}
String signerInfo = verifyDocumentSignResponse.getSignerInfo();
Map<String, String> signerInfoMap = parseKeyValuePairs(signerInfo);
String chiefMiddleName = chiefModel.getPerson().getMiddleName();
String chiefLastName = chiefModel.getPerson().getLastName();
String chiefFirstName = chiefModel.getPerson().getFirstName();
if (signerInfoMap.get("SN").equalsIgnoreCase(chiefLastName) && signerInfoMap.get("G")
.equalsIgnoreCase(chiefFirstName + " " + chiefMiddleName) && signerInfoMap.get("O")
.equalsIgnoreCase(uploadOrgInfo.getOrgName())) {
Map<String, String> hashMap = this.webDavClient.uploadFiles(Map.of(
"fileUrl", multipartFile,
"signUrl", signFile
));
String fileUploadUrl = hashMap.get("fileUrl");
String signFileUploadUrl = hashMap.get("signFile");
fileStatus.setStatus(fileUploadUrl == null || signFileUploadUrl == null
? "Невозможно проверить файл ЛК РП"
: "Загрузка");
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
convertToFio(personModel.getFirstName(), personModel.getMiddleName(),
@ -106,67 +172,154 @@ public class EmployeeInfoFileUploadService {
),
ervuId
);
long fileSize = multipartFile.getSize();
String departureDateTime = DateUtils.convertToString(now);
fileInfo.setFileStatus(fileStatus);
signFileInfo.setFileStatus(fileStatus);
if (fileUploadUrl == null || signFileUploadUrl == null) {
LOGGER.error("Failed to upload files: {}, {}", fileName, signFileName);
fileStatus.setCode(FILE_NOT_CHECKED.getCode());
fileStatus.setDescription(
"Невозможно проверить файл по причине недоступности или ошибки в работе антивируса");
auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo});
return false;
}
else {
fileInfo.setFileUrl(fileUploadUrl);
signFileInfo.setFileUrl(signFileUploadUrl);
EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage(
fileId,
fileUploadUrl,
fileName,
employeeInfoFileFormType,
departureDateTime,
accessToken,
offset,
fileStatus,
ervuId,
esiaUserId,
personModel,
fileSize
);
if (fileUploadUrl != null) {
uploadOrgInfo, new FileInfo[] {
fileInfo, signFileInfo
});
fileStatus.setCode(FILE_UPLOADED.getCode());
fileStatus.setDescription("Файл принят до проверки на вирусы");
String jsonMessage = getJsonKafkaMessage(kafkaMessage);
return sendMessage(jsonMessage);
}
}
else {
LOGGER.error("Failed to upload file: {}", fileName);
if (mchdFile == null) {
fileStatus.setStatus("Некорректная МЧД");
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
convertToFio(personModel.getFirstName(), personModel.getMiddleName(),
personModel.getLastName()
),
ervuId
);
auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {
fileInfo, signFileInfo
});
//Заменить на свою ошибку
throw new RuntimeException();
}
String mchdFileId = UUID.randomUUID().toString();
String mchdFileName = mchdFile.getOriginalFilename();
long mchdFileSize = mchdFile.getSize();
FileInfo mchdFileInfo = employeeInfoKafkaMessageService.getFileInfo(mchdFileId, null,
mchdFileName, employeeInfoFileFormType, departureDateTime, offset, fileStatus,
mchdFileSize
);
if (validateMchd(mchdFile, accessToken)) {
Map<String, String> hashMap = this.webDavClient.uploadFiles(Map.of(
"fileUrl", multipartFile,
"signUrl", signFile,
"mchdFile", mchdFile
));
String fileUploadUrl = hashMap.get("fileUrl");
String signFileUploadUrl = hashMap.get("signFile");
String mchdFileUploadUrl = hashMap.get("mchdFile");
fileStatus.setStatus(
fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null
? "Невозможно проверить файл ЛК РП"
: "Загрузка");
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
convertToFio(personModel.getFirstName(), personModel.getMiddleName(),
personModel.getLastName()
),
ervuId
);
fileInfo.setFileStatus(fileStatus);
signFileInfo.setFileStatus(fileStatus);
if (fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null) {
LOGGER.error("Failed to upload files: {}, {}, {}", fileName, signFileName,
mchdFileName
);
fileStatus.setCode(FILE_NOT_CHECKED.getCode());
fileStatus.setDescription("Невозможно проверить файл по причине недоступности или ошибки в работе антивируса");
auditService.processUploadEvent(kafkaMessage.getOrgInfo(), kafkaMessage.getFileInfo());
fileStatus.setDescription(
"Невозможно проверить файл по причине недоступности или ошибки в работе антивируса");
auditService.processUploadEvent(uploadOrgInfo,
new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo}
);
return false;
}
else {
fileInfo.setFileUrl(fileUploadUrl);
signFileInfo.setFileUrl(signFileUploadUrl);
mchdFileInfo.setFileUrl(mchdFileUploadUrl);
EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage(
uploadOrgInfo, new FileInfo[] {
fileInfo, signFileInfo, mchdFileInfo
});
fileStatus.setCode(FILE_UPLOADED.getCode());
fileStatus.setDescription("Файл принят до проверки на вирусы");
String jsonMessage = getJsonKafkaMessage(kafkaMessage);
return sendMessage(jsonMessage);
}
}
else {
fileStatus.setStatus("Некорректная МЧД");
interactionService.setStatus(fileId, fileStatus.getStatus(), fileName,
employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now),
convertToFio(personModel.getFirstName(), personModel.getMiddleName(),
personModel.getLastName()
),
ervuId
);
auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {
fileInfo, signFileInfo, mchdFileInfo
});
//Заменить на свою ошибку
throw new RuntimeException();
}
}
}
private boolean isValid(MultipartFile multipartFile) {
if (multipartFile == null || multipartFile.getOriginalFilename() == null) {
private boolean isValidCsvWithSig(MultipartFile file, MultipartFile signFile) {
if (file == null || signFile == null || file.getOriginalFilename() == null
|| signFile.getOriginalFilename() == null) {
return false;
}
String fileName = multipartFile.getOriginalFilename();
try {
String contentType = new Tika().detect(multipartFile.getBytes());
Tika tika = new Tika();
MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes();
MimeType mimeType = defaultMimeTypes.forName(contentType);
boolean isCsv = mimeType.getType().equals(MediaType.TEXT_PLAIN)
&& fileName.toLowerCase(Locale.getDefault()).endsWith(".csv");
try {
String fileName = file.getOriginalFilename().toLowerCase(Locale.getDefault());
String signFileName = signFile.getOriginalFilename().toLowerCase(Locale.getDefault());
String fileContentType = tika.detect(file.getBytes());
String signContentType = tika.detect(signFile.getBytes());
MimeType fileMimeType = defaultMimeTypes.forName(fileContentType);
MimeType signMimeType = defaultMimeTypes.forName(signContentType);
boolean isCsv =
MediaType.TEXT_PLAIN.equals(fileMimeType.getType()) && fileName.endsWith(".csv");
boolean isSig =
"application/pkcs7-signature".equals(signMimeType.toString()) && signFileName.endsWith(
".sig");
if (!isCsv) {
LOGGER.info("Trying to upload file={} with wrong mime type={}",
fileName, mimeType
);
LOGGER.info("Invalid main file: name={}, mimeType={}", fileName, fileMimeType);
}
return isCsv;
if (!isSig) {
LOGGER.info("Invalid signature file: name={}, mimeType={}", signFileName, signMimeType);
}
catch (MimeTypeException e) {
LOGGER.error(
"Couldn't get mime type from bytes for file=" + fileName, e);
return false;
return isCsv && isSig;
}
catch (IOException e) {
LOGGER.error("Error while checking file type, file=" + fileName,
e
catch (MimeTypeException | IOException e) {
LOGGER.error("Failed to process files: {}, {}", file.getOriginalFilename(),
signFile.getOriginalFilename(), e
);
return false;
}
@ -206,14 +359,14 @@ public class EmployeeInfoFileUploadService {
try {
DownloadResponse downloadResponse = mapper.readValue(kafkaMessage, DownloadResponse.class);
FileInfo fileInfo = downloadResponse.fileInfo();
FileInfo fileInfo = downloadResponse.filesInfo()[0];
String statusCode = fileInfo.getFileStatus().getCode();
if (Arrays.asList(FILE_INFECTED.getCode(), FILE_CLEAN.getCode()).contains(statusCode)) {
interactionService.delete(fileInfo.getFileId(), downloadResponse.orgInfo().getOrgId());
}
else if (statusCode.equals(FILE_NOT_CHECKED.getCode())) {
auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.fileInfo());
auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo());
interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(),
downloadResponse.orgInfo().getOrgId()
);
@ -223,4 +376,66 @@ public class EmployeeInfoFileUploadService {
throw new JsonParsingException(String.format("Fail get json from: %s", kafkaMessage), e);
}
}
private VerifyDocumentSignResponse validateSign(MultipartFile file, MultipartFile signFile) {
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost upload = new HttpPost(esiaConfig.getSignUrl());
HttpEntity multipart = MultipartEntityBuilder.create()
.addBinaryBody("data", file.getBytes(), ContentType.APPLICATION_OCTET_STREAM,
file.getOriginalFilename()
)
.addBinaryBody("sign", signFile.getBytes(), ContentType.APPLICATION_OCTET_STREAM,
signFile.getOriginalFilename()
)
.build();
upload.setEntity(multipart);
try (CloseableHttpResponse response = httpClient.execute(upload)) {
int statusCode = response.getStatusLine().getStatusCode();
String body = EntityUtils.toString(response.getEntity());
if (statusCode != 200) {
throw new RuntimeException(statusCode + " " + body);
}
return objectMapper.readValue(body, VerifyDocumentSignResponse.class);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private boolean validateMchd(MultipartFile mchdFile, String accessToken) {
try {
String mchdGuid = getMchdGuid(mchdFile);
MchdInfoModel mchdInfoModel = ulDataService.getMchdInfo(mchdGuid, accessToken);
mchdInfoModel.
}
catch (Exception e) {
throw new EsiaException(e);
}
}
private Map<String, String> parseKeyValuePairs(String input) {
Map<String, String> map = new HashMap<>();
String[] pairs = input.split(",");
for (String pair : pairs) {
String[] keyValue = pair.split("=", 2);
if (keyValue.length == 2) {
map.put(keyValue[0].trim(), keyValue[1].trim());
}
}
return map;
}
private String getMchdGuid(MultipartFile mcnhdFile) {
try {
return "asdasdasd";
}
catch (Exception e) {
throw new EsiaException(e);
}
}
}

View file

@ -29,6 +29,7 @@ public class EmployeeInfoKafkaMessageService {
PersonModel personModel, long fileSize) {
return new EmployeeInfoKafkaMessage(
getOrgInfo(accessToken, ervuId, prnOid, personModel),
new FileInfo[] {
getFileInfo(
fileId,
fileUrl,
@ -38,11 +39,16 @@ public class EmployeeInfoKafkaMessageService {
offset,
fileStatus,
fileSize
)
),
}
);
}
private FileInfo getFileInfo(String fileId, String fileUrl, String fileName,
public EmployeeInfoKafkaMessage getKafkaMessage(UploadOrgInfo orgInfo, FileInfo[] filesInfo) {
return new EmployeeInfoKafkaMessage(orgInfo, filesInfo);
}
public FileInfo getFileInfo(String fileId, String fileUrl, String fileName,
EmployeeInfoFileFormType formType, String departureDateTime, String offset,
FileStatus fileStatus, long fileSize) {
return new FileInfo(
@ -58,7 +64,7 @@ public class EmployeeInfoKafkaMessageService {
);
}
private UploadOrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) {
public UploadOrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) {
OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken);
SenderInfo senderInfo = new SenderInfo();
senderInfo.setFirstName(personModel.getFirstName());

View file

@ -10,11 +10,11 @@ import ervu.model.fileupload.UploadOrgInfo;
public class AuditUploadEvent {
private UploadOrgInfo orgInfo;
private FileInfo fileInfo;
private FileInfo[] filesInfo;
public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) {
public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) {
this.orgInfo = orgInfo;
this.fileInfo = fileInfo;
this.filesInfo = filesInfo;
}
public UploadOrgInfo getOrgInfo() {
@ -25,11 +25,11 @@ public class AuditUploadEvent {
this.orgInfo = orgInfo;
}
public FileInfo getFileInfo() {
return fileInfo;
public FileInfo[] getFilesInfo() {
return filesInfo;
}
public void setFileInfo(FileInfo fileInfo) {
this.fileInfo = fileInfo;
public void setFilesInfo(FileInfo[] filesInfo) {
this.filesInfo = filesInfo;
}
}

View file

@ -16,7 +16,7 @@ public interface AuditService {
void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid, String status,
String eventType);
void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo);
void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo);
void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName,
int formatRegistry, String status, String s3FileUrl);

View file

@ -110,10 +110,10 @@ public class BaseAuditService implements AuditService {
}
@Override
public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) {
public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) {
AuditUploadEvent auditUploadEvent = new AuditUploadEvent(
orgInfo,
fileInfo
filesInfo
);
String message = convertToMessage(auditUploadEvent);

View file

@ -0,0 +1,439 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MchdInfoModel implements Serializable {
private static final long serialVersionUID = 1L;
private List<String> stateFacts;
private String guid;
private String number;
private String status;
private String issuedOn;
private String expiredOn;
private Principals principals;
private Agents agents;
private Systems systems;
private Revocations revocations;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getIssuedOn() {
return issuedOn;
}
public void setIssuedOn(String issuedOn) {
this.issuedOn = issuedOn;
}
public String getExpiredOn() {
return expiredOn;
}
public void setExpiredOn(String expiredOn) {
this.expiredOn = expiredOn;
}
public Principals getPrincipals() {
return principals;
}
public void setPrincipals(Principals principals) {
this.principals = principals;
}
public Agents getAgents() {
return agents;
}
public void setAgents(Agents agents) {
this.agents = agents;
}
public Systems getSystems() {
return systems;
}
public void setSystems(Systems systems) {
this.systems = systems;
}
public Revocations getRevocations() {
return revocations;
}
public void setRevocations(Revocations revocations) {
this.revocations = revocations;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Principals implements Serializable {
private List<String> stateFacts;
private int size;
private List<PrincipalElement> elements;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public List<PrincipalElement> getElements() {
return elements;
}
public void setElements(List<PrincipalElement> elements) {
this.elements = elements;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class PrincipalElement implements Serializable {
private List<String> stateFacts;
private Organization organization;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Agents implements Serializable {
private List<String> stateFacts;
private int size;
private List<AgentElement> elements;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public List<AgentElement> getElements() {
return elements;
}
public void setElements(List<AgentElement> elements) {
this.elements = elements;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class AgentElement implements Serializable {
private List<String> stateFacts;
private Person person;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Systems implements Serializable {
private List<String> stateFacts;
private int size;
private List<SystemElement> elements;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public List<SystemElement> getElements() {
return elements;
}
public void setElements(List<SystemElement> elements) {
this.elements = elements;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class SystemElement implements Serializable {
private String nsiId;
private String name;
public String getNsiId() {
return nsiId;
}
public void setNsiId(String nsiId) {
this.nsiId = nsiId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Revocations implements Serializable {
private List<String> stateFacts;
private int size;
private List<RevocationElement> elements;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public List<RevocationElement> getElements() {
return elements;
}
public void setElements(List<RevocationElement> elements) {
this.elements = elements;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class RevocationElement implements Serializable {
private String number;
private String poaGuid;
private String revokedOn;
private Principals principals;
private Agents agents;
private Signatory signatory;
private Systems systems;
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getPoaGuid() {
return poaGuid;
}
public void setPoaGuid(String poaGuid) {
this.poaGuid = poaGuid;
}
public String getRevokedOn() {
return revokedOn;
}
public void setRevokedOn(String revokedOn) {
this.revokedOn = revokedOn;
}
public Principals getPrincipals() {
return principals;
}
public void setPrincipals(Principals principals) {
this.principals = principals;
}
public Agents getAgents() {
return agents;
}
public void setAgents(Agents agents) {
this.agents = agents;
}
public Signatory getSignatory() {
return signatory;
}
public void setSignatory(Signatory signatory) {
this.signatory = signatory;
}
public Systems getSystems() {
return systems;
}
public void setSystems(Systems systems) {
this.systems = systems;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Organization implements Serializable {
private String ogrn;
private String name;
private String inn;
private String kpp;
public String getOgrn() {
return ogrn;
}
public void setOgrn(String ogrn) {
this.ogrn = ogrn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInn() {
return inn;
}
public void setInn(String inn) {
this.inn = inn;
}
public String getKpp() {
return kpp;
}
public void setKpp(String kpp) {
this.kpp = kpp;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Person implements Serializable {
private String snils;
public String getSnils() {
return snils;
}
public void setSnils(String snils) {
this.snils = snils;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Signatory implements Serializable {
private List<String> stateFacts;
private Person person;
public List<String> getStateFacts() {
return stateFacts;
}
public void setStateFacts(List<String> stateFacts) {
this.stateFacts = stateFacts;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
}

View file

@ -0,0 +1,35 @@
package ru.micord.ervu.security.esia.model;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Eduard Tihomirov
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class VerifyDocumentSignResponse implements Serializable {
private static final long serialVersionUID = 1L;
@JsonProperty("error_code")
private String errorCode;
@JsonProperty("signer_subject")
private String signerInfo;
public String getSignVerifyResult() {
return errorCode;
}
public void setSignVerifyResult(String signVerifyResult) {
this.errorCode = signVerifyResult;
}
public String getSignerInfo() {
return signerInfo;
}
public void setSignerInfo(String signerInfo) {
this.signerInfo = signerInfo;
}
}

View file

@ -855,7 +855,7 @@
overflow: hidden;
padding-left: 56px;
}
.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-name::before {
.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .icon-csv::before {
position: absolute;
content: url(../img/svg/file-csv.svg);
top: -2px;

View file

@ -18,7 +18,7 @@
hidden>
<div class="selected-file-list" *ngIf="isFilesListVisible">
<div class="selected-file" *ngFor="let item of uploader.queue">
<span class="selected-file-name">{{item?.file?.name}}</span>
<span class="selected-file-name" [ngClass]="getFileIconClass()">{{item?.file?.name}}</span>
<span class="selected-file-size" *ngIf="displayFileSize">{{item?.file?.size/1024/1024 | number: '.2'}} MB</span>
<button class="selected-file-delete-btn" (click)="removeFile(item)">{{removeFileButtonName}}</button>
</div>

View file

@ -5,7 +5,7 @@ import {
Event,
MessagesService,
UnsupportedOperationError,
AppConfigService
AppConfigService, ObjectRef
} from "@webbpm/base-package";
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core";
import {FileItem, FileUploader} from "ng2-file-upload";
@ -23,6 +23,10 @@ import {TokenConstants} from "../../../modules/security/TokenConstants";
export class ErvuFileUpload extends InputControl {
private static readonly BACKEND_URL: string = "backend.context";
@ObjectRef()
public signFileUploadRef: ErvuFileUpload;
@ObjectRef()
public mchdFileUploadRef: ErvuFileUpload;
@NotNull("true")
public selectFileFieldText: string;
@NotNull("true")
@ -137,6 +141,13 @@ export class ErvuFileUpload extends InputControl {
private setUploaderMethods() {
this.uploader.onBeforeUploadItem = (fileItem: FileItem) => {
const additionalParams: any = {
signFile: this.signFileUploadRef.uploader.queue[0]._file
};
if (this.mchdFileUploadRef && this.mchdFileUploadRef.uploader.queue.length > 0) {
additionalParams.mchdFile = this.mchdFileUploadRef.uploader.queue[0]._file;
}
//refresh headers
this.uploader.setOptions({
@ -153,7 +164,8 @@ export class ErvuFileUpload extends InputControl {
name: TokenConstants.CSRF_HEADER_NAME,
value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME)
}
]
],
additionalParameter: additionalParams
});
this.fileUploadStartEvent.trigger();
this.isDropZoneVisible = false;
@ -221,6 +233,13 @@ export class ErvuFileUpload extends InputControl {
return undefined;
}
getFileIconClass(): string {
if (this.extensionFilter && this.extensionFilter.length > 0) {
return `icon-${this.extensionFilter[0].toLowerCase()}`;
}
return 'icon-default';
}
subscribeToModelChange() {
//empty because there is no ngModel here
}

View file

@ -300,6 +300,11 @@
<artifactId>sardine</artifactId>
<version>5.12</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>

View file

@ -1241,7 +1241,7 @@
<entry>
<key>initialValue</key>
<value>
<simple>"Перед выбором файла убедитесь, что все данные в файле введены корректно"</simple>
<simple>"Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан усиленной квалифицированной электронной подписью."</simple>
</value>
</entry>
</properties>
@ -1359,38 +1359,7 @@
<componentRootId>529140ee-17b6-44e9-8f2d-a097b4bc044d</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="e82af49b-78b1-4c4e-9c9f-7f6afb76a1a2" removed="false">
<value>
<simple>"subtitle"</simple>
</value>
</item>
<item id="633b0a83-ad8e-4b77-8de6-e168bc259852" removed="true"/>
<item id="b714e743-c151-44d6-9bb0-73128652ef6a" removed="true"/>
<item id="1d9468cf-d5b9-4a44-a93b-66d0ab93bf67" removed="true"/>
<item id="c663a654-405f-4e92-8d8d-015bc9d3f68a" removed="true"/>
<item id="9eaf8023-8bf9-4bf2-8cd9-d1a3394837dd" removed="true"/>
</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>
<removed>true</removed>
</children>
<children id="9bd6e5a3-bc0c-45e9-9a40-977161e8f4b2">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
@ -1477,6 +1446,523 @@
<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="7ea732e4-71c7-4325-8569-462acbea89b8">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
<componentRootId>7ea732e4-71c7-4325-8569-462acbea89b8</componentRootId>
<name>Вертикальный контейнер</name>
<container>true</container>
<removed>true</removed>
</children>
</children>
<children id="7ea732e4-71c7-4325-8569-462acbea89b8">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
<componentRootId>7ea732e4-71c7-4325-8569-462acbea89b8</componentRootId>
<name>Вертикальный контейнер</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
<scripts id="87f3fefa-b77b-4137-aab6-b2bcd83ce380"/>
<scripts id="ef21ca22-3f81-4484-ba6f-58d670c12d4f"/>
<scripts id="277e6fbc-9e2e-4080-bf20-5d8be18e6764"/>
<children id="529140ee-17b6-44e9-8f2d-a097b4bc044d">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>529140ee-17b6-44e9-8f2d-a097b4bc044d</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="e82af49b-78b1-4c4e-9c9f-7f6afb76a1a2" removed="false">
<value>
<simple>"subtitle"</simple>
</value>
</item>
<item id="633b0a83-ad8e-4b77-8de6-e168bc259852" removed="true"/>
<item id="b714e743-c151-44d6-9bb0-73128652ef6a" removed="true"/>
<item id="1d9468cf-d5b9-4a44-a93b-66d0ab93bf67" removed="true"/>
<item id="c663a654-405f-4e92-8d8d-015bc9d3f68a" removed="true"/>
<item id="9eaf8023-8bf9-4bf2-8cd9-d1a3394837dd" removed="true"/>
</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="7d338f47-6d12-4040-ba18-f31f520dce8d">
<prototypeId>5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c</prototypeId>
<componentRootId>7d338f47-6d12-4040-ba18-f31f520dce8d</componentRootId>
<name>FileUploadV2</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="36fe7f3d-d9d5-472b-ad12-956ef734ee76">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="ed12dc10-ce08-4b21-80bb-dd0d8b5a00d9" removed="false">
<value>
<simple>"btn-main"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>extensionFilter</key>
<value>
<item id="56e3af9e-cb8f-4c47-9c78-f2e6406c3a89" removed="false">
<value>
<simple>"csv"</simple>
</value>
</item>
<item id="21d726cb-4c32-4243-89ec-020609ecf402" removed="true"/>
</value>
</entry>
<entry>
<key>formType</key>
<value>
<simple>"FORM_9"</simple>
</value>
</entry>
<entry>
<key>maxFileSizeMb</key>
<value>
<simple>5.0</simple>
</value>
</entry>
<entry>
<key>maxFilesToUpload</key>
<value>
<simple>1.0</simple>
</value>
</entry>
<entry>
<key>mchdFileUploadRef</key>
<value>
<simple>{"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>removeFileButtonName</key>
<value>
<simple>"Удалить"</simple>
</value>
</entry>
<entry>
<key>selectFileButtonName</key>
<value>
<simple>"Выбрать"</simple>
</value>
</entry>
<entry>
<key>selectFileFieldText</key>
<value>
<simple>"Перетащите файл или выберите на компьютере"</simple>
</value>
</entry>
<entry>
<key>signFileUploadRef</key>
<value>
<simple>{"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="4333be98-584a-4d30-9427-4d4f7a8b7f7e">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>4333be98-584a-4d30-9427-4d4f7a8b7f7e</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="5c6f9f41-feca-4984-9b40-397063d9f339" removed="false">
<value>
<simple>"mute"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Поддерживаемый формат файла - csv"</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>
<children id="b8373653-133f-43cd-a8c2-ba83f232d379">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
<componentRootId>b8373653-133f-43cd-a8c2-ba83f232d379</componentRootId>
<name>Вертикальный контейнер</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
<scripts id="87f3fefa-b77b-4137-aab6-b2bcd83ce380"/>
<scripts id="ef21ca22-3f81-4484-ba6f-58d670c12d4f"/>
<scripts id="277e6fbc-9e2e-4080-bf20-5d8be18e6764"/>
<children id="bc8596fe-1e46-4cf9-b306-43498f61909c">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>bc8596fe-1e46-4cf9-b306-43498f61909c</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="e82af49b-78b1-4c4e-9c9f-7f6afb76a1a2" removed="false">
<value>
<simple>"subtitle"</simple>
</value>
</item>
<item id="633b0a83-ad8e-4b77-8de6-e168bc259852" removed="true"/>
<item id="b714e743-c151-44d6-9bb0-73128652ef6a" removed="true"/>
<item id="1d9468cf-d5b9-4a44-a93b-66d0ab93bf67" removed="true"/>
<item id="c663a654-405f-4e92-8d8d-015bc9d3f68a" removed="true"/>
<item id="9eaf8023-8bf9-4bf2-8cd9-d1a3394837dd" removed="true"/>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Ваша открепленная усиленная квалифицированная электронная подпись в формате sig"</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="409c0323-011e-4416-a631-f8f852299e1f">
<prototypeId>5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c</prototypeId>
<componentRootId>409c0323-011e-4416-a631-f8f852299e1f</componentRootId>
<name>FileUploadV2</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="36fe7f3d-d9d5-472b-ad12-956ef734ee76">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="ed12dc10-ce08-4b21-80bb-dd0d8b5a00d9" removed="false">
<value>
<simple>"btn-main"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>extensionFilter</key>
<value>
<item id="56e3af9e-cb8f-4c47-9c78-f2e6406c3a89" removed="true"/>
<item id="21d726cb-4c32-4243-89ec-020609ecf402" removed="false">
<value>
<simple>"sig"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>formType</key>
<value>
<simple>"FORM_9"</simple>
</value>
</entry>
<entry>
<key>maxFileSizeMb</key>
<value>
<simple>5.0</simple>
</value>
</entry>
<entry>
<key>maxFilesToUpload</key>
<value>
<simple>1.0</simple>
</value>
</entry>
<entry>
<key>removeFileButtonName</key>
<value>
<simple>"Удалить"</simple>
</value>
</entry>
<entry>
<key>selectFileButtonName</key>
<value>
<simple>"Выбрать"</simple>
</value>
</entry>
<entry>
<key>selectFileFieldText</key>
<value>
<simple>"Перетащите файл или выберите на компьютере"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="7f70dc30-8116-47da-ad8d-01cec26a60d7">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>7f70dc30-8116-47da-ad8d-01cec26a60d7</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="5c6f9f41-feca-4984-9b40-397063d9f339" removed="false">
<value>
<simple>"mute"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Поддерживаемый формат файла - sig"</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="c4d501b1-7565-45c3-8722-d624126f6cfb">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
<componentRootId>c4d501b1-7565-45c3-8722-d624126f6cfb</componentRootId>
<name>Вертикальный контейнер</name>
<container>true</container>
<removed>true</removed>
</children>
</children>
<children id="c4d501b1-7565-45c3-8722-d624126f6cfb">
<prototypeId>9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91</prototypeId>
<componentRootId>c4d501b1-7565-45c3-8722-d624126f6cfb</componentRootId>
<name>Вертикальный контейнер</name>
<container>true</container>
<childrenReordered>false</childrenReordered>
<scripts id="bf098f19-480e-44e4-9084-aa42955c4d0f"/>
<scripts id="72befe90-1915-483f-b88c-d1ec5d4bdc8e"/>
<scripts id="87f3fefa-b77b-4137-aab6-b2bcd83ce380"/>
<scripts id="ef21ca22-3f81-4484-ba6f-58d670c12d4f"/>
<scripts id="277e6fbc-9e2e-4080-bf20-5d8be18e6764"/>
<children id="e958f2ee-e112-4bef-9c8a-40e2f8278ca9">
<prototypeId>8b755f7b-e52b-4800-830a-f01467cd5cbb</prototypeId>
<componentRootId>e958f2ee-e112-4bef-9c8a-40e2f8278ca9</componentRootId>
<name>Check box</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="26133e8f-65d7-44ca-a0a4-68db3f5b62a3"/>
<scripts id="86e89129-ae47-46eb-80bf-cc2f085b417d"/>
<scripts id="a0756916-bdba-4c9b-bbb4-a7a6b44ecdd1"/>
<scripts id="2db943c0-0818-47b0-9d50-28d6314ed50a"/>
<scripts id="4a2eb40d-0aa7-441a-8c1b-e8bd28420123"/>
</children>
<children id="f0794dfe-f0e9-446b-8029-c4ff9640f650">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>f0794dfe-f0e9-446b-8029-c4ff9640f650</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="e82af49b-78b1-4c4e-9c9f-7f6afb76a1a2" removed="false">
<value>
<simple>"subtitle"</simple>
</value>
</item>
<item id="633b0a83-ad8e-4b77-8de6-e168bc259852" removed="true"/>
<item id="b714e743-c151-44d6-9bb0-73128652ef6a" removed="true"/>
<item id="1d9468cf-d5b9-4a44-a93b-66d0ab93bf67" removed="true"/>
<item id="c663a654-405f-4e92-8d8d-015bc9d3f68a" removed="true"/>
<item id="9eaf8023-8bf9-4bf2-8cd9-d1a3394837dd" removed="true"/>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД"</simple>
</value>
</entry>
<entry>
<key>label</key>
<value>
<simple>null</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="1dc25461-a60e-456b-93cb-cc0f28dc347b">
<prototypeId>5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c</prototypeId>
<componentRootId>1dc25461-a60e-456b-93cb-cc0f28dc347b</componentRootId>
<name>FileUploadV2</name>
<container>false</container>
<expanded>false</expanded>
<childrenReordered>false</childrenReordered>
<scripts id="36fe7f3d-d9d5-472b-ad12-956ef734ee76">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="ed12dc10-ce08-4b21-80bb-dd0d8b5a00d9" removed="false">
<value>
<simple>"btn-main"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>extensionFilter</key>
<value>
<item id="56e3af9e-cb8f-4c47-9c78-f2e6406c3a89" removed="true"/>
<item id="21d726cb-4c32-4243-89ec-020609ecf402" removed="false">
<value>
<simple>"xml"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>formType</key>
<value>
<simple>"FORM_9"</simple>
</value>
</entry>
<entry>
<key>maxFileSizeMb</key>
<value>
<simple>5.0</simple>
</value>
</entry>
<entry>
<key>maxFilesToUpload</key>
<value>
<simple>1.0</simple>
</value>
</entry>
<entry>
<key>removeFileButtonName</key>
<value>
<simple>"Удалить"</simple>
</value>
</entry>
<entry>
<key>selectFileButtonName</key>
<value>
<simple>"Выбрать"</simple>
</value>
</entry>
<entry>
<key>selectFileFieldText</key>
<value>
<simple>"Перетащите файл или выберите на компьютере"</simple>
</value>
</entry>
</properties>
</scripts>
</children>
<children id="9b446462-16e5-4241-b409-0287dea92b3a">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>9b446462-16e5-4241-b409-0287dea92b3a</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="5c6f9f41-feca-4984-9b40-397063d9f339" removed="false">
<value>
<simple>"mute"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Поддерживаемый формат файла - xml"</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>
@ -1493,73 +1979,35 @@
<componentRootId>7d338f47-6d12-4040-ba18-f31f520dce8d</componentRootId>
<name>FileUploadV2</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="36fe7f3d-d9d5-472b-ad12-956ef734ee76">
<properties>
<entry>
<key>collectible</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>cssClasses</key>
<value>
<item id="ed12dc10-ce08-4b21-80bb-dd0d8b5a00d9" removed="false">
<value>
<simple>"btn-main"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>extensionFilter</key>
<value>
<item id="56e3af9e-cb8f-4c47-9c78-f2e6406c3a89" removed="false">
<value>
<simple>"csv"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>formType</key>
<value>
<simple>"FORM_9"</simple>
</value>
</entry>
<entry>
<key>maxFileSizeMb</key>
<value>
<simple>5.0</simple>
</value>
</entry>
<entry>
<key>maxFilesToUpload</key>
<value>
<simple>1.0</simple>
</value>
</entry>
<entry>
<key>removeFileButtonName</key>
<value>
<simple>"Удалить"</simple>
</value>
</entry>
<entry>
<key>selectFileButtonName</key>
<value>
<simple>"Выбрать"</simple>
</value>
</entry>
<entry>
<key>selectFileFieldText</key>
<value>
<simple>"Перетащите файл или выберите на компьютере"</simple>
</value>
</entry>
</properties>
</scripts>
<removed>true</removed>
</children>
<children id="4333be98-584a-4d30-9427-4d4f7a8b7f7e">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>4333be98-584a-4d30-9427-4d4f7a8b7f7e</componentRootId>
<name>Текст</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="bc8596fe-1e46-4cf9-b306-43498f61909c">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>bc8596fe-1e46-4cf9-b306-43498f61909c</componentRootId>
<name>Текст</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="409c0323-011e-4416-a631-f8f852299e1f">
<prototypeId>5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c</prototypeId>
<componentRootId>409c0323-011e-4416-a631-f8f852299e1f</componentRootId>
<name>FileUploadV2</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="7f70dc30-8116-47da-ad8d-01cec26a60d7">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>7f70dc30-8116-47da-ad8d-01cec26a60d7</componentRootId>
<name>Текст</name>
<container>false</container>
<removed>true</removed>
</children>
<children id="6c7a8ca5-8479-44a0-978c-3af81bc83c88">
<prototypeId>98594cec-0a9b-4cef-af09-e1b71cb2ad9e</prototypeId>
@ -1596,6 +2044,24 @@
</complex>
</value>
</item>
<item id="10c7cb14-3c80-4311-98bc-d56b89359608" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>propertyName</key>
<value>
<simple>"fileAddedEvent"</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
<entry>
@ -1605,6 +2071,108 @@
<entry>
<key>conditions</key>
<value>
<item id="e1ff1623-8abe-4153-b105-b14b174c21b2" removed="false">
<value>
<complex>
<entry>
<key>_isGroupSelected</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>one</key>
<value>
<complex>
<entry>
<key>conditionFirstPart</key>
<value>
<complex>
<entry>
<key>objectValue</key>
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"getValue"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>operation</key>
<value>
<simple>"IS_NOT_EMPTY"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="8af7ee90-86c4-4480-85f8-116c05d8a9c4" removed="false">
<value>
<complex>
<entry>
<key>_isGroupSelected</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>one</key>
<value>
<complex>
<entry>
<key>conditionFirstPart</key>
<value>
<complex>
<entry>
<key>objectValue</key>
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"getValue"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>operation</key>
<value>
<simple>"IS_NOT_EMPTY"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="385e4035-ad63-4556-934e-1b1a43cbf4b8" removed="true"/>
<item id="24585e4e-dd71-4bfc-9aaa-a5583b7e9429" removed="true"/>
</value>
@ -1696,6 +2264,24 @@
</complex>
</value>
</item>
<item id="bb318165-7658-47f4-90e3-efae0d9094d1" removed="false">
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>propertyName</key>
<value>
<simple>"fileDeletedEvent"</simple>
</value>
</entry>
</complex>
</value>
</item>
</value>
</entry>
<entry>
@ -1705,6 +2291,108 @@
<entry>
<key>conditions</key>
<value>
<item id="4b1f523e-f809-4224-a8fb-fa4a5ac7c2f4" removed="false">
<value>
<complex>
<entry>
<key>_isGroupSelected</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>one</key>
<value>
<complex>
<entry>
<key>conditionFirstPart</key>
<value>
<complex>
<entry>
<key>objectValue</key>
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"getValue"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>operation</key>
<value>
<simple>"IS_EMPTY"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="2804bed8-45bf-4a6d-994a-e53550c66152" removed="false">
<value>
<complex>
<entry>
<key>_isGroupSelected</key>
<value>
<simple>false</simple>
</value>
</entry>
<entry>
<key>one</key>
<value>
<complex>
<entry>
<key>conditionFirstPart</key>
<value>
<complex>
<entry>
<key>objectValue</key>
<value>
<complex>
<entry>
<key>behavior</key>
<value>
<simple>{"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"}</simple>
</value>
</entry>
<entry>
<key>method</key>
<value>
<simple>"getValue"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</entry>
<entry>
<key>operation</key>
<value>
<simple>"IS_EMPTY"</simple>
</value>
</entry>
</complex>
</value>
</entry>
</complex>
</value>
</item>
<item id="385e4035-ad63-4556-934e-1b1a43cbf4b8" removed="true"/>
<item id="24585e4e-dd71-4bfc-9aaa-a5583b7e9429" removed="true"/>
</value>
@ -1712,7 +2400,7 @@
<entry>
<key>logicalOperation</key>
<value>
<simple>null</simple>
<simple>"OR"</simple>
</value>
</entry>
</complex>
@ -1850,39 +2538,6 @@
</properties>
</scripts>
</children>
<children id="4333be98-584a-4d30-9427-4d4f7a8b7f7e">
<prototypeId>ba24d307-0b91-4299-ba82-9d0b52384ff2</prototypeId>
<componentRootId>4333be98-584a-4d30-9427-4d4f7a8b7f7e</componentRootId>
<name>Текст</name>
<container>false</container>
<childrenReordered>false</childrenReordered>
<scripts id="cf4526a1-96ab-4820-8aa9-62fb54c2b64c">
<properties>
<entry>
<key>cssClasses</key>
<value>
<item id="5c6f9f41-feca-4984-9b40-397063d9f339" removed="false">
<value>
<simple>"mute"</simple>
</value>
</item>
</value>
</entry>
<entry>
<key>initialValue</key>
<value>
<simple>"Поддерживаемый формат файла - csv"</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="9d068751-8bbe-4fa0-b4b5-fa8e395fa019">
<prototypeId>fd7e47b9-dce1-4d14-9f3a-580c79f59579</prototypeId>
<componentRootId>9d068751-8bbe-4fa0-b4b5-fa8e395fa019</componentRootId>