diff --git a/backend/pom.xml b/backend/pom.xml index 1c99cb0a..d5b6baae 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -97,14 +97,6 @@ org.xerial.snappy snappy-java - - org.apache.kafka - kafka-clients - - - org.xerial.snappy - snappy-java - ru.cg.webbpm.modules inject @@ -202,9 +194,28 @@ org.apache.tika tika-core + + org.apache.httpcomponents + httpmime + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + ${project.parent.artifactId} + + + kr.motd.maven + os-maven-plugin + ${os-maven-plugin.version} + + maven-compiler-plugin @@ -233,6 +244,22 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + compile + + + + + com.google.protobuf:protoc:4.27.3:exe:${os.detected.classifier} + ${project.parent.basedir}/backend/src/main/resources + ${project.parent.basedir}/backend/target/generated-sources/java/protobuf + + diff --git a/backend/src/main/java/ervu/FileConstants.java b/backend/src/main/java/ervu/FileConstants.java new file mode 100644 index 00000000..ba09dc65 --- /dev/null +++ b/backend/src/main/java/ervu/FileConstants.java @@ -0,0 +1,10 @@ +package ervu; + +/** + * @author Eduard Tihomirov + */ +public final class FileConstants { + public static final String DOCUMENT = "DOCUMENT"; + public static final String SIGNATURE = "SIGNATURE"; + public static final String MCHD = "MCHD"; +} diff --git a/backend/src/main/java/ervu/AvKafkaConfig.java b/backend/src/main/java/ervu/FileKafkaConfig.java similarity index 90% rename from backend/src/main/java/ervu/AvKafkaConfig.java rename to backend/src/main/java/ervu/FileKafkaConfig.java index 09d4ad30..c5c31c46 100644 --- a/backend/src/main/java/ervu/AvKafkaConfig.java +++ b/backend/src/main/java/ervu/FileKafkaConfig.java @@ -23,7 +23,7 @@ import org.springframework.kafka.core.ProducerFactory; * @author Alexandr Shalaginov */ @Configuration -public class AvKafkaConfig { +public class FileKafkaConfig { @Value("${kafka.hosts}") private String kafkaUrl; @Value("${kafka.auth_sec_proto}") @@ -38,7 +38,7 @@ public class AvKafkaConfig { private String saslMechanism; @Bean - public ProducerFactory avProducerFactory() { + public ProducerFactory fileProducerFactory() { return new DefaultKafkaProducerFactory<>(producerConfigs()); } @@ -58,7 +58,7 @@ public class AvKafkaConfig { } @Bean - public ConsumerFactory avConsumerFactory() { + public ConsumerFactory fileConsumerFactory() { return new DefaultKafkaConsumerFactory<>(consumerConfigs()); } @@ -77,16 +77,16 @@ public class AvKafkaConfig { return props; } - @Bean("avContainerFactory") + @Bean("fileContainerFactory") public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); - factory.setConsumerFactory(avConsumerFactory()); + factory.setConsumerFactory(fileConsumerFactory()); return factory; } - @Bean("avTemplate") + @Bean("fileTemplate") public KafkaTemplate kafkaTemplate() { - return new KafkaTemplate<>(avProducerFactory()); + return new KafkaTemplate<>(fileProducerFactory()); } } diff --git a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java index 12721d12..06ce0a68 100644 --- a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java +++ b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java @@ -15,7 +15,9 @@ import java.time.Duration; import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -89,12 +91,20 @@ 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 uploadFiles(Map files) { + String fileCatalog = UUID.randomUUID() + "/"; Sardine sardine = initClient(username, password); try { - return putAndGetUrl(multipartFile.getBytes(), fileName, sardine); + Map result = new HashMap<>(); + for (Map.Entry entry : files.entrySet()) { + String key = entry.getKey(); + MultipartFile file = entry.getValue(); + if (file != null) { + result.put(key, putAndGetUrl(file.getBytes(), getNewFilename(file.getOriginalFilename()), sardine, fileCatalog)); + } + } + return result; } catch (IOException e) { throw new WebDavException("Failed to put file into WebDAV", e); @@ -115,7 +125,7 @@ public class WebDavClient { return UUID.randomUUID() + fileExtension; } - public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client) throws IOException { + public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client, String fileCatalog) throws IOException { if (badServersCache.size() == urls.length) { return null; } @@ -140,19 +150,23 @@ public class WebDavClient { } boolean isBad = false; String serverUrl = server.getUrl(); - String fileUploadUrl = serverUrl + "/" + fileName; - + String directory = serverUrl + "/" + fileCatalog + "/"; + if (!client.exists(directory)) { + client.createDirectory(directory); + } + String fileUploadUrl = directory + fileName; try { client.put(fileUploadUrl, fileBytes); server.setLastCallTime(System.currentTimeMillis()); } catch (ConnectException | ClientProtocolException ignore) { isBad = true; + LOGGER.error("WebDAV error. Url: {}, Message: {}", fileUploadUrl, ignore.getMessage());; } if (isBad) { badServersCache.getUnchecked(serverUrl); - return putAndGetUrl(fileBytes, fileName, client); + return putAndGetUrl(fileBytes, fileName, client, fileCatalog); } return fileUploadUrl; diff --git a/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java b/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java index 204f7429..219d8807 100644 --- a/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java +++ b/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java @@ -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."); } diff --git a/backend/src/main/java/ervu/enums/FileStatusCode.java b/backend/src/main/java/ervu/enums/FileStatusCode.java index 9fa3f241..6c507920 100644 --- a/backend/src/main/java/ervu/enums/FileStatusCode.java +++ b/backend/src/main/java/ervu/enums/FileStatusCode.java @@ -7,7 +7,10 @@ public enum FileStatusCode { FILE_UPLOADED("01"), FILE_INFECTED("02"), FILE_CLEAN("03"), - FILE_NOT_CHECKED("11"); + FILE_ACCEPTED("04"), + FILE_NOT_CHECKED("11"), + SIGN_INVALID("12"), + MCHD_INVALID("13"); private final String code; diff --git a/backend/src/main/java/ervu/exception/FileUploadException.java b/backend/src/main/java/ervu/exception/FileUploadException.java new file mode 100644 index 00000000..cdddfd7e --- /dev/null +++ b/backend/src/main/java/ervu/exception/FileUploadException.java @@ -0,0 +1,19 @@ +package ervu.exception; + +/** + * @author Eduard Tihomirov + */ +public class FileUploadException extends RuntimeException{ + + public FileUploadException(String message) { + super(message); + } + + public FileUploadException(String message, Throwable cause) { + super(message, cause); + } + + public FileUploadException(Throwable cause) { + super(cause); + } +} diff --git a/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java b/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java index 883f749b..297e6399 100644 --- a/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java +++ b/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java @@ -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) { } diff --git a/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java b/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java index a6f1e08f..a3d27410 100644 --- a/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java +++ b/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java @@ -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) + '}'; } } diff --git a/backend/src/main/java/ervu/model/fileupload/FileInfo.java b/backend/src/main/java/ervu/model/fileupload/FileInfo.java index 5141e433..4119ad1f 100644 --- a/backend/src/main/java/ervu/model/fileupload/FileInfo.java +++ b/backend/src/main/java/ervu/model/fileupload/FileInfo.java @@ -15,13 +15,14 @@ public class FileInfo { private String departureDateTime; private String timeZone; private FileStatus fileStatus; + private String type; public FileInfo() { } public FileInfo(String fileId, String fileUrl, String fileName, String filePatternCode, String filePatternName, String fileSize, String departureDateTime, String timeZone, - FileStatus fileStatus) { + FileStatus fileStatus, String type) { this.fileId = fileId; this.fileUrl = fileUrl; this.fileName = fileName; @@ -31,6 +32,7 @@ public class FileInfo { this.departureDateTime = departureDateTime; this.timeZone = timeZone; this.fileStatus = fileStatus; + this.type = type; } public String getFileId() { @@ -69,6 +71,22 @@ public class FileInfo { return fileStatus; } + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + 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; diff --git a/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java b/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java new file mode 100644 index 00000000..ac6a7a85 --- /dev/null +++ b/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java @@ -0,0 +1,7 @@ +package ervu.model.fileupload; + +/** + * @author Eduard Tihomirov + */ +public record FileStatusResponse(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { +} diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index c9eb916e..bb2c4dec 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -5,17 +5,32 @@ import java.nio.charset.StandardCharsets; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.UUID; +import javax.xml.parsers.DocumentBuilderFactory; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import ervu.client.fileupload.WebDavClient; +import ervu.exception.FileUploadException; 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.FileStatusResponse; +import ervu.model.fileupload.UploadOrgInfo; +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; @@ -26,14 +41,21 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.support.MessageSourceAccessor; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import org.w3c.dom.Document; +import org.w3c.dom.Node; import ru.micord.ervu.audit.service.AuditService; import ru.micord.ervu.exception.JsonParsingException; +import ru.micord.ervu.kafka.service.ReplyingKafkaService; +import ru.micord.ervu.security.esia.config.EsiaConfig; 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; @@ -41,10 +63,18 @@ import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil; import ru.micord.ervu.service.InteractionService; import ru.micord.ervu.util.DateUtils; -import static ervu.enums.FileStatusCode.FILE_CLEAN; +import ru.cg.webbpm.modules.core.runtime.api.LocalizedException; +import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils; + +import static ervu.FileConstants.DOCUMENT; +import static ervu.FileConstants.MCHD; +import static ervu.FileConstants.SIGNATURE; +import static ervu.enums.FileStatusCode.FILE_ACCEPTED; import static ervu.enums.FileStatusCode.FILE_INFECTED; import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED; import static ervu.enums.FileStatusCode.FILE_UPLOADED; +import static ervu.enums.FileStatusCode.MCHD_INVALID; +import static ervu.enums.FileStatusCode.SIGN_INVALID; import static ru.micord.ervu.util.StringUtils.convertToFio; /** @@ -53,127 +83,529 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; @Service public class EmployeeInfoFileUploadService { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class); + private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor( + "messages/common_errors_messages"); private final WebDavClient webDavClient; private final EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService; + private final ReplyingKafkaService replyingKafkaService; private final KafkaTemplate kafkaTemplate; 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; + @Value("${av.kafka.clear.s3}") + private String kafkaClearS3Topic; + + @Value("${ervu.kafka.download.request}") + private String kafkaDownloadRequestTopic; + + @Value("${av.kafka.download.request}") + private String kafkaRequestTopic; + + @Value("${av.kafka.download.response}") + private String kafkaResponseTopic; public EmployeeInfoFileUploadService( WebDavClient webDavClient, EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService, - @Qualifier("avTemplate") KafkaTemplate kafkaTemplate, + ReplyingKafkaService replyingKafkaService, InteractionService interactionService, UlDataService ulDataService, - AuditService auditService) { + AuditService auditService, + ObjectMapper objectMapper, + EsiaConfig esiaConfig, + @Qualifier("fileTemplate") KafkaTemplate kafkaTemplate) { this.webDavClient = webDavClient; this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService; - this.kafkaTemplate = kafkaTemplate; + this.replyingKafkaService = replyingKafkaService; this.interactionService = interactionService; this.ulDataService = ulDataService; this.auditService = auditService; + this.objectMapper = objectMapper; + this.esiaConfig = esiaConfig; + this.kafkaTemplate = kafkaTemplate; } - public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType, - String offset) { - UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); + public boolean saveEmployeeInformationFiles(MultipartFile multipartFile, + MultipartFile signFile, MultipartFile mchdFile, String formType, + String offset) { - if (userIdsPair == null || !isValid(multipartFile)) { + UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); + LocalDateTime now = LocalDateTime.now(); + String departureDateTime = DateUtils.convertToString(now); + + if (userIdsPair == null || !isValidCsvWithSig(multipartFile, signFile)) { return false; } - String fileId = UUID.randomUUID().toString(); - String fileName = multipartFile.getOriginalFilename(); - EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf( - formType); String esiaUserId = userIdsPair.getEsiaUserId(); String ervuId = userIdsPair.getErvuId(); String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId); + EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken); PersonModel personModel = employeeModel.getPerson(); + EmployeeModel chiefModel = ulDataService.getChiefEmployeeModel(accessToken); + + String fio = convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() + ); + + UploadOrgInfo uploadOrgInfo = employeeInfoKafkaMessageService.getOrgInfo(accessToken, ervuId, + esiaUserId, personModel + ); + + EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType); + + FileInfo fileInfo = createFileInfo(multipartFile, employeeInfoFileFormType, + departureDateTime, offset, DOCUMENT + ); + FileInfo signFileInfo = createFileInfo(signFile, employeeInfoFileFormType, + departureDateTime, offset, SIGNATURE + ); + FileInfo mchdFileInfo = mchdFile != null ? + createFileInfo(mchdFile, employeeInfoFileFormType, departureDateTime, + offset, MCHD + ) : null; - String fileUploadUrl = this.webDavClient.uploadFile(multipartFile); FileStatus fileStatus = new FileStatus(); - fileStatus.setStatus(fileUploadUrl == null ? "Невозможно проверить файл ЛК РП" : "Загрузка"); - LocalDateTime now = LocalDateTime.now(); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId + Map uploadResults = uploadFiles(multipartFile, signFile, mchdFile); + + String fileUploadUrl = uploadResults.get("fileUrl"); + String signFileUploadUrl = uploadResults.get("signFile"); + String mchdFileUploadUrl = uploadResults.get("mchdFile"); + + boolean uploadSuccess = checkUploadSuccess(fileUploadUrl, signFileUploadUrl, mchdFileUploadUrl, + mchdFile != null ); - long fileSize = multipartFile.getSize(); - String departureDateTime = DateUtils.convertToString(now); - EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( - fileId, - fileUploadUrl, - fileName, - employeeInfoFileFormType, - departureDateTime, - accessToken, - offset, - fileStatus, - ervuId, - esiaUserId, - personModel, - fileSize + fileStatus.setStatus(uploadSuccess ? "Загрузка" : "Невозможно проверить файл ЛК РП"); + interactionService.setStatus(fileInfo.getFileId(), fileStatus.getStatus(), + multipartFile.getOriginalFilename(), employeeInfoFileFormType.getFilePatternCode(), + Timestamp.valueOf(now), fio, ervuId ); - if (fileUploadUrl != null) { - fileStatus.setCode(FILE_UPLOADED.getCode()); - fileStatus.setDescription("Файл принят до проверки на вирусы"); - String jsonMessage = getJsonKafkaMessage(kafkaMessage); - return sendMessage(jsonMessage); + fileInfo.setFileStatus(fileStatus); + signFileInfo.setFileStatus(fileStatus); + if (mchdFileInfo != null) { + mchdFileInfo.setFileStatus(fileStatus); + } + + if (!uploadSuccess) { + handleUploadFailure(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, + multipartFile.getOriginalFilename(), signFile.getOriginalFilename(), + mchdFile != null ? mchdFile.getOriginalFilename() : null + ); + return false; + } + + fileInfo.setFileUrl(fileUploadUrl); + signFileInfo.setFileUrl(signFileUploadUrl); + if (mchdFileInfo != null) { + mchdFileInfo.setFileUrl(mchdFileUploadUrl); + } + + FileInfo[] fileInfos = mchdFileInfo != null ? + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : + new FileInfo[] {fileInfo, signFileInfo}; + + String response = sendKafkaMessage(uploadOrgInfo, fileInfos, fileStatus); + + DownloadResponse downloadResponse; + try { + downloadResponse = processMessageFromAv(response); + } + catch (JsonProcessingException e) { + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setStatus("Невозможно проверить файл ЛК РП"); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); + interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), + uploadOrgInfo.getOrgId() + ); + auditService.processUploadEvent(uploadOrgInfo, fileInfos, fileStatus); + throw new FileUploadException(e); + } + + fileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> DOCUMENT.equals(file.getType())) + .findFirst() + .orElse(fileInfo); + + signFileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> SIGNATURE.equals(file.getType())) + .findFirst() + .orElse(signFileInfo); + + mchdFileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> MCHD.equals(file.getType())) + .findFirst() + .orElse(mchdFileInfo); + VerifyDocumentSignResponse verifyDocumentSignResponse; + try { + verifyDocumentSignResponse = validateSign(multipartFile, signFile); + } + catch (Exception e) { + handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); + throw e; + } + + return validateSignerAndMchd(verifyDocumentSignResponse, chiefModel, uploadOrgInfo, + mchdFile, accessToken, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + ); + } + + private FileInfo createFileInfo(MultipartFile file, EmployeeInfoFileFormType formType, + String departureDateTime, String offset, String fileType) { + + String fileId = UUID.randomUUID().toString(); + return employeeInfoKafkaMessageService.getFileInfo(fileId, null, file.getOriginalFilename(), + formType, departureDateTime, offset, null, file.getSize(), fileType + ); + } + + private Map uploadFiles(MultipartFile multipartFile, MultipartFile signFile, + MultipartFile mchdFile) { + + Map filesToUpload = new HashMap<>(); + filesToUpload.put("fileUrl", multipartFile); + filesToUpload.put("signFile", signFile); + + if (mchdFile != null) { + filesToUpload.put("mchdFile", mchdFile); + } + + return this.webDavClient.uploadFiles(filesToUpload); + } + + private boolean checkUploadSuccess(String fileUploadUrl, String signFileUploadUrl, + String mchdFileUploadUrl, boolean hasMchdFile) { + + if (hasMchdFile) { + return fileUploadUrl != null && signFileUploadUrl != null && mchdFileUploadUrl != null; } else { - LOGGER.error("Failed to upload file: {}", fileName); - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setDescription("Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); - auditService.processUploadEvent(kafkaMessage.getOrgInfo(), kafkaMessage.getFileInfo()); - return false; + return fileUploadUrl != null && signFileUploadUrl != null; } } - private boolean isValid(MultipartFile multipartFile) { + private void handleUploadFailure(FileStatus fileStatus, UploadOrgInfo uploadOrgInfo, + FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, + String fileName, String signFileName, String mchdFileName) { - if (multipartFile == null || multipartFile.getOriginalFilename() == null) { + LOGGER.error("Failed to upload files: {}, {}, {}", fileName, signFileName, mchdFileName); + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); + + FileInfo[] fileInfos = mchdFileInfo != null ? + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : + new FileInfo[] {fileInfo, signFileInfo}; + + auditService.processUploadEvent(uploadOrgInfo, fileInfos, fileStatus); + } + + private String sendKafkaMessage(UploadOrgInfo uploadOrgInfo, FileInfo[] fileInfos, + FileStatus fileStatus) { + + EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( + uploadOrgInfo, fileInfos); + + fileStatus.setCode(FILE_UPLOADED.getCode()); + fileStatus.setDescription("Файл принят до проверки на вирусы"); + + String jsonMessage = getJsonKafkaMessage(kafkaMessage); + return replyingKafkaService.sendMessageAndGetReply(kafkaRequestTopic, + kafkaResponseTopic, jsonMessage + ); + } + + private boolean validateSignerAndMchd(VerifyDocumentSignResponse verifyDocumentSignResponse, + EmployeeModel chiefModel, UploadOrgInfo uploadOrgInfo, MultipartFile mchdFile, + String accessToken, FileInfo fileInfo, FileInfo signFileInfo, + FileInfo mchdFileInfo, String ervuId, String response) { + + String signerInfo = verifyDocumentSignResponse.getSignerInfo(); + Map signerInfoMap = parseKeyValuePairs(signerInfo); + + String chiefMiddleName = chiefModel.getPerson().getMiddleName(); + String chiefLastName = chiefModel.getPerson().getLastName(); + String chiefFirstName = chiefModel.getPerson().getFirstName(); + + boolean isSignerValid = signerInfoMap.get("SN").equalsIgnoreCase(chiefLastName) && + signerInfoMap.get("G") + .equalsIgnoreCase(chiefFirstName + " " + chiefMiddleName) && + signerInfoMap.get("O").equalsIgnoreCase(uploadOrgInfo.getOrgName()); + + if (isSignerValid) { + FileStatusResponse fileStatusResponse = new FileStatusResponse(uploadOrgInfo, + new FileInfo[] {fileInfo, signFileInfo}, fileInfo.getFileStatus() + ); + try { + if (sendMessage(fileStatusResponse)) { + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); + return true; + } + else { + interactionService.delete(fileInfo.getFileId(), ervuId); + return false; + } + } + catch (JsonProcessingException e) { + handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); + throw new JsonParsingException( + String.format("Fail get json from: %s", fileStatusResponse), e); + } + } + + if (mchdFile == null) { + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, null, ervuId, response + ); + throw new LocalizedException("mchd_null", MESSAGE_SOURCE); + } + FileStatusResponse fileStatusResponse = new FileStatusResponse(uploadOrgInfo, + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo}, fileInfo.getFileStatus() + ); + try { + validateMchd(mchdFile, accessToken, signerInfoMap.get("SN") + " " + signerInfoMap.get("G"), + chiefFirstName, chiefLastName, chiefMiddleName + ); + + if (sendMessage(fileStatusResponse)) { + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); + return true; + } + else { + interactionService.delete(fileInfo.getFileId(), ervuId); + return false; + } + } + catch (JsonProcessingException e) { + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + ); + throw new JsonParsingException( + String.format("Fail get json from: %s", fileStatusResponse), e); + } + catch (Exception e) { + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + ); + throw e; + } + } + + private void handleMchdValidationError(UploadOrgInfo uploadOrgInfo, + FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, String ervuId, String response) { + FileStatus packInfo = new FileStatus(); + packInfo.setCode(MCHD_INVALID.getCode()); + packInfo.setStatus("Некорректная МЧД"); + packInfo.setDescription("Неуспешная проверка МЧД"); + + interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), ervuId); + FileInfo[] fileInfos; + if (mchdFileInfo != null) { + mchdFileInfo.setFileStatus(packInfo); + fileInfos = new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo}; + } + else { + fileInfos = new FileInfo[] {fileInfo, signFileInfo}; + } + + auditService.processUploadEvent(uploadOrgInfo, fileInfos, packInfo); + clearS3(response); + } + + private boolean isValidCsvWithSig(MultipartFile file, MultipartFile signFile) { + if (file == null || signFile == null || file.getOriginalFilename() == null + || signFile.getOriginalFilename() == null) { return false; } - String fileName = multipartFile.getOriginalFilename(); + Tika tika = new Tika(); + MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes(); + try { - String contentType = new Tika().detect(multipartFile.getBytes()); - MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes(); - MimeType mimeType = defaultMimeTypes.forName(contentType); - boolean isCsv = mimeType.getType().equals(MediaType.TEXT_PLAIN) - && fileName.toLowerCase(Locale.getDefault()).endsWith(".csv"); + 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); + } + + return isCsv && isSig; } - catch (MimeTypeException e) { - LOGGER.error( - "Couldn't get mime type from bytes for file=" + fileName, e); - return false; - } - 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; } } - private boolean sendMessage(String message) { - ProducerRecord record = new ProducerRecord<>(this.kafkaTopicName, message); + private String getJsonKafkaMessage(EmployeeInfoKafkaMessage employeeInfoKafkaMessage) { + try { + return objectMapper.writeValueAsString(employeeInfoKafkaMessage); + } + catch (JsonProcessingException e) { + throw new JsonParsingException( + String.format("Fail get json from: %s", employeeInfoKafkaMessage), e); + } + } + + @KafkaListener(id = "${av.kafka.group.id}", topics = "${ervu.kafka.download.response}", + containerFactory = "fileContainerFactory") + public void listenKafka(String kafkaMessage) { + try { + FileStatusResponse fileStatusResponse = objectMapper.readValue(kafkaMessage, FileStatusResponse.class); + FileInfo fileInfo = Arrays.stream(fileStatusResponse.filesInfo()) + .filter(fileInfo1 -> fileInfo1.getType().equals(DOCUMENT)) + .findFirst() + .get(); + String statusCode = fileStatusResponse.packInfo().getCode(); + + if (FILE_ACCEPTED.getCode().equals(statusCode)) { + interactionService.delete(fileInfo.getFileId(), fileStatusResponse.orgInfo().getOrgId()); + } + } + catch (JsonProcessingException e) { + 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.getFileSignVerifyUrl()); + 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 == 401) { + throw new LocalizedException("file_sign_validate", MESSAGE_SOURCE); + } + if (statusCode == 500) { + try { + String errorCode = objectMapper.readTree(body) + .get("error_code") + .asText(); + switch (errorCode) { + case "CERT_TRUST_REVOCATION_STATUS_UNKNOWN" -> + throw new LocalizedException("cert_trust_revocation", MESSAGE_SOURCE); + case "CERT_TRUST_IS_UNTRUSTED_ROOT" -> + throw new LocalizedException("cert_untrusted_root", MESSAGE_SOURCE); + case "CERT_TRUST_IS_NOT_TIME_VALID" -> + throw new LocalizedException("cert_is_not_time_valid", MESSAGE_SOURCE); + default -> throw new FileUploadException( + "Unknown 500 error in verify module. Error code " + errorCode); + } + } + catch (LocalizedException e) { + throw e; + } + catch (Exception e) { + throw new FileUploadException("Unknown 500 error in verify module. Response body " + body, e); + } + } + if (statusCode != 200) { + throw new FileUploadException("Unknown error in verify module. StatusCode " + statusCode + " .Response body " + body); + } + return objectMapper.readValue(body, VerifyDocumentSignResponse.class); + } + } + catch (IOException e) { + throw new FileUploadException("Failed to process sign module response ", e); + } + } + + private void validateMchd(MultipartFile mchdFile, String accessToken, String agentFio, + String chiefFirstName, String chiefLastName, String chiefMiddleName) { + String mchdGuid; + try { + mchdGuid = getMchdGuid(mchdFile); + } + catch (Exception e) { + throw new LocalizedException("mchd_cant_parse", MESSAGE_SOURCE); + } + MchdInfoModel mchdInfoModel = ulDataService.getMchdInfoModel(mchdGuid, accessToken); + if (!mchdInfoModel.getStatus().equals("A")) { + throw new LocalizedException("mchd_expired", MESSAGE_SOURCE); + } + boolean validAgent = mchdInfoModel.getAgents() + .getElements() + .stream() + .anyMatch(agent1 -> agentFio.equalsIgnoreCase( + agent1.getPerson().getLastName() + " " + agent1.getPerson().getFirstName() + " " + + agent1.getPerson().getMiddleName())); + if (!validAgent) { + throw new LocalizedException("mchd_validate_agent", MESSAGE_SOURCE); + } + while (mchdInfoModel.getParentGuid() != null) { + mchdInfoModel = ulDataService.getMchdInfoModel(mchdInfoModel.getParentGuid(), accessToken); + if (!mchdInfoModel.getStatus().equals("A")) { + throw new LocalizedException("mchd_tree_expired", MESSAGE_SOURCE); + } + } + MchdInfoModel.Element principal = mchdInfoModel.getPrincipals().getElements().get(0); + MchdInfoModel.Person chief = principal.getOrganization().getChief(); + boolean principalFioEquals = chief.getFirstName().equalsIgnoreCase(chiefFirstName) && + chief.getLastName().equalsIgnoreCase(chiefLastName) && + chief.getMiddleName().equalsIgnoreCase(chiefMiddleName); + if (!principalFioEquals) { + throw new LocalizedException("mchd_validate_principal", MESSAGE_SOURCE); + } + } + + private Map parseKeyValuePairs(String input) { + Map 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 mchdFile) throws Exception { + Document doc = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + .parse(mchdFile.getInputStream()); + doc.getDocumentElement().normalize(); + Node node = doc.getElementsByTagName("СвДов").item(0); + if (node != null && node.getAttributes().getNamedItem("НомДовер") != null) { + return node.getAttributes().getNamedItem("НомДовер").getNodeValue(); + } + else { + throw new FileUploadException("Cannot parse mchd guid"); + } + } + + private boolean sendMessage(FileStatusResponse fileStatusResponse) throws JsonProcessingException { + String message = objectMapper.writeValueAsString(fileStatusResponse); + ProducerRecord record = new ProducerRecord<>(this.kafkaDownloadRequestTopic, message); record.headers() .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); @@ -188,39 +620,55 @@ public class EmployeeInfoFileUploadService { } } - private String getJsonKafkaMessage(EmployeeInfoKafkaMessage employeeInfoKafkaMessage) { - ObjectMapper mapper = new ObjectMapper(); + private void clearS3(String message) { + ProducerRecord record = new ProducerRecord<>(this.kafkaClearS3Topic, message); + record.headers() + .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); try { - return mapper.writeValueAsString(employeeInfoKafkaMessage); + this.kafkaTemplate.send(record).get(); } - catch (JsonProcessingException e) { - throw new JsonParsingException( - String.format("Fail get json from: %s", employeeInfoKafkaMessage), e); + catch (Exception exception) { + LOGGER.error("Failed to clear s3", exception); } } - @KafkaListener(id = "${av.kafka.group.id}", topics = "${av.kafka.download.response}", - containerFactory = "avContainerFactory") - public void listenKafka(String kafkaMessage) { - ObjectMapper mapper = new ObjectMapper(); - - try { - DownloadResponse downloadResponse = mapper.readValue(kafkaMessage, DownloadResponse.class); - FileInfo fileInfo = downloadResponse.fileInfo(); - 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()); - interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), - downloadResponse.orgInfo().getOrgId() - ); - } + private DownloadResponse processMessageFromAv(String response) throws JsonProcessingException { + DownloadResponse downloadResponse = objectMapper.readValue(response, DownloadResponse.class); + FileInfo avFile = Arrays.stream(downloadResponse.filesInfo()) + .filter(fileInfo1 -> fileInfo1.getType().equals(DOCUMENT)) + .findAny() + .get(); + FileStatus fileStatus1 = avFile.getFileStatus(); + if (fileStatus1.getCode().equals(FILE_INFECTED.getCode())) { + interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), + downloadResponse.orgInfo().getOrgId()); + FileStatusResponse fileStatusResponse = new FileStatusResponse(downloadResponse.orgInfo(), + downloadResponse.filesInfo(), fileStatus1 + ); + sendMessage(fileStatusResponse); + throw new LocalizedException("av_file_infected", MESSAGE_SOURCE); } - catch (JsonProcessingException e) { - throw new JsonParsingException(String.format("Fail get json from: %s", kafkaMessage), e); + else if (fileStatus1.getCode().equals(FILE_NOT_CHECKED.getCode())) { + interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), + downloadResponse.orgInfo().getOrgId()); + auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo(), fileStatus1); + throw new FileUploadException("File not checked: " + avFile.getFileName()); } + return downloadResponse; + } + + private void handeSignError(FileInfo fileInfo, FileInfo signFileInfo, UploadOrgInfo uploadOrgInfo, String response) { + FileStatus packInfo = new FileStatus(); + packInfo.setCode(SIGN_INVALID.getCode()); + packInfo.setStatus("Некорректная ЭП"); + packInfo.setDescription("Неуспешная проверка ЭП"); + signFileInfo.setFileStatus(packInfo); + + interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), + uploadOrgInfo.getOrgId() + ); + + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}, packInfo); + clearS3(response); } } diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java index d8414f2b..4e6b86bd 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java @@ -26,25 +26,32 @@ public class EmployeeInfoKafkaMessageService { public EmployeeInfoKafkaMessage getKafkaMessage(String fileId, String fileUrl, String fileName, EmployeeInfoFileFormType formType, String departureDateTime, String accessToken, String offset, FileStatus fileStatus, String ervuId, String prnOid, - PersonModel personModel, long fileSize) { + PersonModel personModel, long fileSize, String type) { return new EmployeeInfoKafkaMessage( getOrgInfo(accessToken, ervuId, prnOid, personModel), - getFileInfo( - fileId, - fileUrl, - fileName, - formType, - departureDateTime, - offset, - fileStatus, - fileSize - ) + new FileInfo[] { + getFileInfo( + fileId, + fileUrl, + fileName, + formType, + departureDateTime, + offset, + fileStatus, + fileSize, + type + ), + } ); } - 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) { + FileStatus fileStatus, long fileSize, String type) { return new FileInfo( fileId, fileUrl, @@ -54,11 +61,12 @@ public class EmployeeInfoKafkaMessageService { String.valueOf(fileSize), departureDateTime, offset, - fileStatus + fileStatus, + type ); } - 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()); diff --git a/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java b/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java index bc06ec83..b4559353 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java +++ b/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java @@ -21,10 +21,10 @@ public final class AuditConstants { ); private static final Map downloadTypes = Map.of( - 1, "Выписка из журнала взаимодействия ЮЛ" + 1, "Выписка из журнала взаимодействия ЮЛ", + 2, "Выгрузка описания ошибок по непринятым записям" ); - private AuditConstants() { } diff --git a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java index 65270471..e7346494 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java +++ b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java @@ -2,6 +2,7 @@ package ru.micord.ervu.audit.model; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; /** @@ -10,11 +11,13 @@ import ervu.model.fileupload.UploadOrgInfo; public class AuditUploadEvent { private UploadOrgInfo orgInfo; - private FileInfo fileInfo; + private FileInfo[] filesInfo; + private FileStatus packInfo; - public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) { + public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { this.orgInfo = orgInfo; - this.fileInfo = fileInfo; + this.filesInfo = filesInfo; + this.packInfo = packInfo; } public UploadOrgInfo getOrgInfo() { @@ -25,11 +28,19 @@ 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; + } + + public FileStatus getPackInfo() { + return packInfo; + } + + public void setPackInfo(FileStatus packInfo) { + this.packInfo = packInfo; } } diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java index d9f9ea8d..fec9091c 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java @@ -3,6 +3,7 @@ package ru.micord.ervu.audit.service; import javax.servlet.http.HttpServletRequest; 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.OrgInfo; @@ -16,7 +17,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, FileStatus packInfo); void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, int formatRegistry, String status, String s3FileUrl); diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java index c9777870..40aad7a8 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Conditional; @@ -110,10 +111,11 @@ public class BaseAuditService implements AuditService { } @Override - public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) { + public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { AuditUploadEvent auditUploadEvent = new AuditUploadEvent( orgInfo, - fileInfo + filesInfo, + packInfo ); String message = convertToMessage(auditUploadEvent); diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java index b45a907e..ca82cab4 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java @@ -3,6 +3,7 @@ package ru.micord.ervu.audit.service.impl; import javax.servlet.http.HttpServletRequest; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; @@ -26,7 +27,7 @@ public class StubAuditService implements AuditService { String status, String eventType) {} @Override - public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo) {} + public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo, FileStatus packInfo) {} @Override public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, diff --git a/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java new file mode 100644 index 00000000..c3b160aa --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java @@ -0,0 +1,88 @@ +package ru.micord.ervu.controller; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; + +import com.google.protobuf.ByteString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +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.model.ValidateExportResponse; +import ru.micord.ervu.security.webbpm.jwt.UserIdsPair; +import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil; +import ru.micord.ervu.service.ValidationFileService; + +@RestController +@RequestMapping("/validate") +public class ValidationFileController { + private static final Logger LOGGER = LoggerFactory.getLogger(ValidationFileController.class); + private final AuditService auditService; + private final ValidationFileService validationFileService; + + public ValidationFileController(AuditService auditService, + ValidationFileService validationFileService) { + this.auditService = auditService; + this.validationFileService = validationFileService; + } + + @GetMapping("/export/{fileId}") + public ResponseEntity exportFile(HttpServletRequest servletRequest, + @PathVariable String fileId) { + int size = 0; + String fileName = null; + String status = null; + + try { + if (!StringUtils.hasText(fileId)) { + return ResponseEntity.notFound().build(); + } + + UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); + String ervuId = userIdsPair.getErvuId(); + ValidateExportResponse validateExportResponse = validationFileService.exportFile(ervuId, + fileId + ); + + if (!validateExportResponse.hasFile()) { + LOGGER.error("Response does not contain file content for fileId: {}, user: {}", fileId, ervuId); + status = AuditConstants.FAILURE_STATUS_TYPE; + return ResponseEntity.noContent().build(); + } + + ByteString file = validateExportResponse.getFile(); + size = file.size(); + fileName = validateExportResponse.getFileName(); + String encodedFilename = URLEncoder.encode(fileName, StandardCharsets.UTF_8); + InputStreamResource resource = new InputStreamResource(file.newInput()); + + status = AuditConstants.SUCCESS_STATUS_TYPE; + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename*=UTF-8''" + encodedFilename + ) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } + catch (Exception e) { + if (status == null) { + status = AuditConstants.FAILURE_STATUS_TYPE; + } + throw e; + } + finally { + auditService.processDownloadEvent(servletRequest, size, fileName, 2, status, null); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/exception/ExportException.java b/backend/src/main/java/ru/micord/ervu/exception/ExportException.java new file mode 100644 index 00000000..5603f06f --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/exception/ExportException.java @@ -0,0 +1,14 @@ +package ru.micord.ervu.exception; + +/** + * @author Adel Kalimullin + */ +public class ExportException extends RuntimeException { + public ExportException(String message) { + super(message); + } + + public ExportException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java index 6adc2e2f..cbc23a70 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java @@ -2,14 +2,18 @@ package ru.micord.ervu.journal; public class JournalDto { + private Integer documentNumber; private String fileId; private String departureDateTime; private String fileName; private Integer filePatternCode; private String senderFio; private String status; - public Integer filesSentCount; - public Integer acceptedFilesCount; + private Integer rowsCount; + private Integer rowsSuccess; + private Integer rowsError; + private Boolean hasFailedRows; + public String getDepartureDateTime() { return departureDateTime; @@ -65,21 +69,48 @@ public class JournalDto { return this; } - public Integer getFilesSentCount() { - return filesSentCount; + public Integer getRowsCount() { + return rowsCount; } - public JournalDto setFilesSentCount(Integer filesSentCount) { - this.filesSentCount = filesSentCount; + public JournalDto setRowsCount(Integer rowsCount) { + this.rowsCount = rowsCount; return this; } - public Integer getAcceptedFilesCount() { - return acceptedFilesCount; + public Integer getRowsSuccess() { + return rowsSuccess; } - public JournalDto setAcceptedFilesCount(Integer acceptedFilesCount) { - this.acceptedFilesCount = acceptedFilesCount; + public JournalDto setRowsSuccess(Integer rowsSuccess) { + this.rowsSuccess = rowsSuccess; + return this; + } + + public Integer getRowsError() { + return rowsError; + } + + public JournalDto setRowsError(Integer rowsError) { + this.rowsError = rowsError; + return this; + } + + public Integer getDocumentNumber() { + return documentNumber; + } + + public JournalDto setDocumentNumber(Integer documentNumber) { + this.documentNumber = documentNumber; + return this; + } + + public Boolean isHasFailedRows() { + return hasFailedRows; + } + + public JournalDto setHasFailedRows(Boolean hasFailedRows) { + this.hasFailedRows = hasFailedRows; return this; } } diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java index 8cb0fafe..410a8e74 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java @@ -1,6 +1,7 @@ package ru.micord.ervu.journal; import java.time.LocalDateTime; +import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -9,78 +10,19 @@ import ru.micord.ervu.journal.deserializer.DepartureDateTimeDeserializer; @JsonIgnoreProperties(ignoreUnknown = true) public class JournalFileInfo { - private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) - private String fileName; // Название файла - private Integer filePatternCode; // Номер шаблона(Формы) - private String filePatternName; - @JsonDeserialize(using = DepartureDateTimeDeserializer.class) - private LocalDateTime departureDateTime; // Дата-время отправки файла - private String timeZone; //Таймзона - private JournalFileStatus fileStatus; + private List packFiles; // Список файлов (csv, sig, mchd) + private JournalFileStatus packInfo; //общий статус по пакету private SenderInfo senderInfo; private Integer rowsCount; //Общее количество записей отправленных в файле private Integer rowsSuccess; //Количество записей принятых в файле + private Integer rowsError; //Количество записей непринятых в файле - public String getFileId() { - return fileId; + public List getPackFiles() { + return packFiles; } - public JournalFileInfo setFileId(String fileId) { - this.fileId = fileId; - return this; - } - - public String getFileName() { - return fileName; - } - - public JournalFileInfo setFileName(String fileName) { - this.fileName = fileName; - return this; - } - - public Integer getFilePatternCode() { - return filePatternCode; - } - - public JournalFileInfo setFilePatternCode(Integer filePatternCode) { - this.filePatternCode = filePatternCode; - return this; - } - - public String getFilePatternName() { - return filePatternName; - } - - public JournalFileInfo setFilePatternName(String filePatternName) { - this.filePatternName = filePatternName; - return this; - } - - public LocalDateTime getDepartureDateTime() { - return departureDateTime; - } - - public JournalFileInfo setDepartureDateTime(LocalDateTime departureDateTime) { - this.departureDateTime = departureDateTime; - return this; - } - - public String getTimeZone() { - return timeZone; - } - - public JournalFileInfo setTimeZone(String timeZone) { - this.timeZone = timeZone; - return this; - } - - public JournalFileStatus getFileStatus() { - return fileStatus; - } - - public JournalFileInfo setFileStatus(JournalFileStatus fileStatus) { - this.fileStatus = fileStatus; + public JournalFileInfo setPackFiles(List packFiles) { + this.packFiles = packFiles; return this; } @@ -110,4 +52,106 @@ public class JournalFileInfo { this.rowsSuccess = rowsSuccess; return this; } -} + + public Integer getRowsError() { + return rowsError; + } + + public JournalFileInfo setRowsError(Integer rowsError) { + this.rowsError = rowsError; + return this; + } + + public JournalFileStatus getPackInfo() { + return packInfo; + } + + public JournalFileInfo setPackInfo(JournalFileStatus packInfo) { + this.packInfo = packInfo; + return this; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class JournalFileDetails { + private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) + private String fileName; // Название файла + private Integer filePatternCode; // Номер шаблона(Формы) + private String filePatternName; + @JsonDeserialize(using = DepartureDateTimeDeserializer.class) + private LocalDateTime departureDateTime; // Дата-время отправки файла + private String timeZone; //Таймзона + private JournalFileStatus fileStatus; + private String type; + + public String getFileId() { + return fileId; + } + + public JournalFileDetails setFileId(String fileId) { + this.fileId = fileId; + return this; + } + + public String getFileName() { + return fileName; + } + + public JournalFileDetails setFileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Integer getFilePatternCode() { + return filePatternCode; + } + + public JournalFileDetails setFilePatternCode(Integer filePatternCode) { + this.filePatternCode = filePatternCode; + return this; + } + + public String getFilePatternName() { + return filePatternName; + } + + public JournalFileDetails setFilePatternName(String filePatternName) { + this.filePatternName = filePatternName; + return this; + } + + public LocalDateTime getDepartureDateTime() { + return departureDateTime; + } + + public JournalFileDetails setDepartureDateTime(LocalDateTime departureDateTime) { + this.departureDateTime = departureDateTime; + return this; + } + + public String getTimeZone() { + return timeZone; + } + + public JournalFileDetails setTimeZone(String timeZone) { + this.timeZone = timeZone; + return this; + } + + public JournalFileStatus getFileStatus() { + return fileStatus; + } + + public JournalFileDetails setFileStatus(JournalFileStatus fileStatus) { + this.fileStatus = fileStatus; + return this; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java index 86bf1ef9..f76f0941 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java +++ b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java @@ -11,20 +11,22 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; public class JournalDtoMapper { - public static JournalDto mapToJournalDto(JournalFileInfo journalFileInfo) { + public static JournalDto mapToJournalDto(JournalFileInfo journalFileInfo, + JournalFileInfo.JournalFileDetails journalFileDetails) { SenderInfo senderInfo = journalFileInfo.getSenderInfo(); return new JournalDto() - .setFileId(journalFileInfo.getFileId()) - .setDepartureDateTime(Timestamp.valueOf(journalFileInfo.getDepartureDateTime()).toString()) - .setFileName(journalFileInfo.getFileName()) - .setFilePatternCode(journalFileInfo.getFilePatternCode()) + .setFileId(journalFileDetails.getFileId()) + .setDepartureDateTime(Timestamp.valueOf(journalFileDetails.getDepartureDateTime()).toString()) + .setFileName(journalFileDetails.getFileName()) + .setFilePatternCode(journalFileDetails.getFilePatternCode()) .setSenderFio(convertToFio(senderInfo.getFirstName(), senderInfo.getMiddleName(), senderInfo.getLastName() ) ) - .setStatus(journalFileInfo.getFileStatus().getStatus()) - .setFilesSentCount(journalFileInfo.getRowsCount()) - .setAcceptedFilesCount(journalFileInfo.getRowsSuccess()); + .setStatus(journalFileInfo.getPackInfo().getStatus()) + .setRowsCount(journalFileInfo.getRowsCount()) + .setRowsSuccess(journalFileInfo.getRowsSuccess()) + .setRowsError(journalFileInfo.getRowsError()); } public static JournalDto mapToJournalDto(InteractionLogRecord record) { @@ -34,8 +36,9 @@ public class JournalDtoMapper { .setFilePatternCode(Integer.valueOf(record.getForm())) .setSenderFio(record.getSender()) .setStatus(record.getStatus()) - .setFilesSentCount(record.getRecordsSent()) - .setAcceptedFilesCount(record.getRecordsAccepted()) + .setRowsCount(0) + .setRowsSuccess(0) + .setRowsError(0) .setFileId(record.getFileId()); } } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java index e33d1c5e..c408bd53 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java @@ -4,8 +4,10 @@ import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.config.SaslConfigs; +import org.apache.kafka.common.serialization.BytesDeserializer; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; +import org.apache.kafka.common.utils.Bytes; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -52,60 +54,104 @@ public class ReplyingKafkaConfig { private String password; @Value("${kafka.auth_sasl_mech}") private String saslMechanism; + @Value("${av.kafka.download.response}") + private String avReplyTopic; + @Value("${ervu.kafka.validate.export.response}") + private String validateReplyTopic; - @Bean("ervuProducerFactory") + @Bean public ProducerFactory producerFactory() { - Map configProps = new HashMap<>(); - configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + Map configProps = commonProducerConfig(); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); - configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" - + username + "\" password=\"" + password + "\";"); - configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); return new DefaultKafkaProducerFactory<>(configProps); } - @Bean - public ConsumerFactory consumerFactory() { + private Map commonProducerConfig() { Map configProps = new HashMap<>(); - configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + "-" + UUID.randomUUID()); - configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); - configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); - configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" - + username + "\" password=\"" + password + "\";"); + configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" + username + "\" password=\"" + password + "\";"); configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return configProps; + } + + @Bean("ervuConsumerFactory") + public ConsumerFactory ervuConsumerFactory() { + Map configProps = commonConsumerConfig(); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(configProps); } + @Bean("validateConsumerFactory") + public ConsumerFactory validateConsumerFactory() { + Map configProps = commonConsumerConfig(); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, BytesDeserializer.class); + return new DefaultKafkaConsumerFactory<>(configProps); + } + + private Map commonConsumerConfig() { + Map configProps = new HashMap<>(); + configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + "-" + UUID.randomUUID()); + configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); + configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" + username + "\" password=\"" + password + "\";"); + configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); + configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); + configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + return configProps; + } + @Bean("ervuContainerFactory") - public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { - ConcurrentKafkaListenerContainerFactory factory = - new ConcurrentKafkaListenerContainerFactory<>(); - factory.setConsumerFactory(consumerFactory()); + public ConcurrentKafkaListenerContainerFactory ervuContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(ervuConsumerFactory()); return factory; } - @Bean - public ConcurrentMessageListenerContainer replyContainer( - @Qualifier("ervuContainerFactory") ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory) { - return kafkaListenerContainerFactory.createContainer(orgReplyTopic, excerptReplyTopic, journalReplyTopic); + @Bean("validateContainerFactory") + public ConcurrentKafkaListenerContainerFactory validateContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(validateConsumerFactory()); + return factory; } - @Bean - public ReplyingKafkaTemplate replyingKafkaTemplate( - @Qualifier("ervuProducerFactory") ProducerFactory producerFactory, - ConcurrentMessageListenerContainer replyContainer) { - ReplyingKafkaTemplate replyingKafkaTemplate = - new ReplyingKafkaTemplate<>(producerFactory, replyContainer); - replyingKafkaTemplate.setCorrelationHeaderName("messageId"); - replyingKafkaTemplate.setCorrelationIdStrategy(record -> + @Bean("ervuContainer") + public ConcurrentMessageListenerContainer ervuContainer( + @Qualifier("ervuContainerFactory") ConcurrentKafkaListenerContainerFactory factory) { + return factory.createContainer(orgReplyTopic, excerptReplyTopic, + journalReplyTopic, avReplyTopic); + } + + @Bean("validateContainer") + public ConcurrentMessageListenerContainer validateContainer( + @Qualifier("validateContainerFactory") ConcurrentKafkaListenerContainerFactory factory) { + return factory.createContainer(validateReplyTopic); + } + + @Bean("ervuReplyingTemplate") + public ReplyingKafkaTemplate ervuReplyingTemplate( + ProducerFactory producerFactory, + @Qualifier("ervuContainer") ConcurrentMessageListenerContainer container) { + ReplyingKafkaTemplate template = new ReplyingKafkaTemplate<>(producerFactory, container); + customizeTemplate(template); + return template; + } + + @Bean("validateReplyingTemplate") + public ReplyingKafkaTemplate validateReplyingTemplate( + ProducerFactory producerFactory, + @Qualifier("validateContainer") ConcurrentMessageListenerContainer container) { + ReplyingKafkaTemplate template = new ReplyingKafkaTemplate<>(producerFactory, container); + customizeTemplate(template); + return template; + } + + private void customizeTemplate(ReplyingKafkaTemplate template) { + template.setCorrelationHeaderName("messageId"); + template.setCorrelationIdStrategy(record -> new CorrelationKey(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8))); - replyingKafkaTemplate.setDefaultReplyTimeout(Duration.ofSeconds(replyTimeout)); - replyingKafkaTemplate.setSharedReplyTopic(true); - return replyingKafkaTemplate; + template.setDefaultReplyTimeout(Duration.ofSeconds(replyTimeout)); + template.setSharedReplyTopic(true); } } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java index e3e41e6a..1488cb8f 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java @@ -11,7 +11,7 @@ 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.RequestMapping; +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; @@ -31,7 +31,7 @@ import ru.micord.ervu.util.UrlUtils; public class ErvuKafkaController { @Autowired - private ReplyingKafkaService replyingKafkaService; + private ReplyingKafkaService replyingKafkaService; @Autowired private AuditService auditService; @@ -48,7 +48,7 @@ public class ErvuKafkaController { @Autowired private ObjectMapper objectMapper; - @RequestMapping(value = "/kafka/excerpt") + @GetMapping(value = "/kafka/excerpt") public ResponseEntity getExcerptFile(HttpServletRequest request) { String fileUrl = null; String fileName = null; diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java index 49918e61..876048d6 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java @@ -1,8 +1,9 @@ package ru.micord.ervu.kafka.service; -public interface ReplyingKafkaService { - String sendMessageAndGetReply(String requestTopic, - String requestReplyTopic, - String requestMessage); -} +public interface ReplyingKafkaService { + + V sendMessageAndGetReply(String requestTopic, + String replyTopic, + T requestMessage); +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java similarity index 54% rename from backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java rename to backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java index 03e19f72..c849daa2 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java @@ -6,12 +6,10 @@ import java.util.concurrent.ExecutionException; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.common.header.internals.RecordHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; import org.springframework.kafka.requestreply.RequestReplyFuture; -import org.springframework.kafka.support.KafkaHeaders; import org.springframework.stereotype.Service; import ru.micord.ervu.kafka.exception.KafkaMessageException; import ru.micord.ervu.kafka.exception.KafkaMessageReplyTimeoutException; @@ -21,35 +19,30 @@ import ru.micord.ervu.kafka.service.ReplyingKafkaService; * @author Eduard Tihomirov */ @Service -public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService { +public abstract class BaseReplyingKafkaService implements ReplyingKafkaService { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final ReplyingKafkaTemplate replyingKafkaTemplate; - public BaseReplyingKafkaServiceImpl( - ReplyingKafkaTemplate replyingKafkaTemplate) { - this.replyingKafkaTemplate = replyingKafkaTemplate; - } - - public String sendMessageAndGetReply(String requestTopic, - String replyTopic, - String requestMessage) { + @Override + public V sendMessageAndGetReply(String requestTopic, String replyTopic, T requestMessage) { long startTime = System.currentTimeMillis(); - ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); - record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); - RequestReplyFuture replyFuture = replyingKafkaTemplate.sendAndReceive(record); + RequestReplyFuture replyFuture = getTemplate().sendAndReceive( + getProducerRecord(requestTopic, replyTopic, requestMessage)); try { - String result = Optional.ofNullable(replyFuture.get()) - .map(ConsumerRecord::value) - .orElseThrow(() -> new KafkaMessageException("Kafka return result is null.")); - LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms", - Thread.currentThread().getId(), System.currentTimeMillis() - startTime); - return result; + ConsumerRecord result = Optional.ofNullable(replyFuture.get()) + .orElseThrow(() -> new KafkaMessageException("Kafka return result is null")); + LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms, replyTopic: {}", + Thread.currentThread().getId(), System.currentTimeMillis() - startTime, replyTopic); + return result.value(); } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms", - Thread.currentThread().getId(), System.currentTimeMillis() - startTime); + LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms, replyTopic: {}", + Thread.currentThread().getId(), System.currentTimeMillis() - startTime, replyTopic); throw new KafkaMessageReplyTimeoutException(e); } } + protected abstract ReplyingKafkaTemplate getTemplate(); + + protected abstract ProducerRecord getProducerRecord(String requestTopic, + String replyTopic, T requestMessage); } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java new file mode 100644 index 00000000..51c3d452 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java @@ -0,0 +1,31 @@ +package ru.micord.ervu.kafka.service.impl; + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.stereotype.Service; + +@Service +public class ErvuReplyingKafkaService extends BaseReplyingKafkaService{ + private final ReplyingKafkaTemplate replyingKafkaTemplate; + + public ErvuReplyingKafkaService( + @Qualifier("ervuReplyingTemplate") ReplyingKafkaTemplate replyingKafkaTemplate) { + this.replyingKafkaTemplate = replyingKafkaTemplate; + } + + @Override + protected ReplyingKafkaTemplate getTemplate() { + return replyingKafkaTemplate; + } + + @Override + protected ProducerRecord getProducerRecord(String requestTopic, String replyTopic, + String requestMessage) { + ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); + record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); + return record; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java new file mode 100644 index 00000000..10903afc --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java @@ -0,0 +1,32 @@ +package ru.micord.ervu.kafka.service.impl; + + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.apache.kafka.common.utils.Bytes; +import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.stereotype.Service; + +@Service +public class ValidateReplyingKafkaService extends BaseReplyingKafkaService { + private final ReplyingKafkaTemplate replyingKafkaTemplate; + + public ValidateReplyingKafkaService( + ReplyingKafkaTemplate replyingKafkaTemplate) { + this.replyingKafkaTemplate = replyingKafkaTemplate; + } + + @Override + protected ReplyingKafkaTemplate getTemplate() { + return replyingKafkaTemplate; + } + + @Override + protected ProducerRecord getProducerRecord(String requestTopic, String replyTopic, + String requestMessage) { + ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); + record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); + return record; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java b/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java new file mode 100644 index 00000000..65dbdd7a --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java @@ -0,0 +1,4 @@ +package ru.micord.ervu.model; + +public record ValidateExportRequest(String orgId, String fileId) { +} diff --git a/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java new file mode 100644 index 00000000..566456f0 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java @@ -0,0 +1,29 @@ +package ru.micord.ervu.model; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import rtl.pgs.ervu.lkrp.journal.proto.ResponseData; + + +public class ValidateExportResponse { + private final String fileName; + private final ByteString file; + + public ValidateExportResponse(byte[] bytes) throws InvalidProtocolBufferException { + ResponseData responseData = ResponseData.parseFrom(bytes); + fileName = responseData.getFileName(); + file = responseData.getFile(); + } + + public String getFileName() { + return fileName; + } + + public ByteString getFile() { + return file; + } + + public boolean hasFile() { + return file != null && !file.isEmpty(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java b/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java index 7795ebc0..8d3f53f1 100644 --- a/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java +++ b/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java @@ -7,5 +7,6 @@ public enum FilterType { TEXT, DATE, NUMBER, - SET + SET, + FILE } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index 20470f54..decebcdb 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -71,6 +71,9 @@ public class EsiaConfig { @Value("${esia.login.attempts.count:5}") private long esiaLoginAttemptsCount; + @Value("${file.sign.verify.url}") + private String fileSignVerifyUrl; + public String getEsiaOrgScopes() { String[] scopeItems = esiaOrgScopes.split(","); return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new)); @@ -146,4 +149,8 @@ public class EsiaConfig { public long getEsiaLoginAttemptsCount() { return esiaLoginAttemptsCount; } + + public String getFileSignVerifyUrl() { + return fileSignVerifyUrl; + } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java new file mode 100644 index 00000000..b2f25961 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java @@ -0,0 +1,148 @@ +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 String guid; + private String status; + private String parentGuid; + private Member principals; + private Member agents; + + public String getGuid() { + return guid; + } + + public void setGuid(String guid) { + this.guid = guid; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getParentGuid() { + return parentGuid; + } + + public void setParentGuid(String parentGuid) { + this.parentGuid = parentGuid; + } + + public Member getPrincipals() { + return principals; + } + + public void setPrincipals(Member principals) { + this.principals = principals; + } + + public Member getAgents() { + return agents; + } + + public void setAgents(Member agents) { + this.agents = agents; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Member implements Serializable { + private List elements; + + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Element implements Serializable { + private Person person; + private Organization organization; + + public Organization getOrganization() { + return organization; + } + + public void setOrganization(Organization organization) { + this.organization = organization; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Organization implements Serializable { + private String name; + private Person chief; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Person getChief() { + return chief; + } + + public void setChief(Person chief) { + this.chief = chief; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Person implements Serializable { + private String firstName; + private String lastName; + private String middleName; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getMiddleName() { + return middleName; + } + + public void setMiddleName(String middleName) { + this.middleName = middleName; + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java new file mode 100644 index 00000000..9485a74c --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java @@ -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; + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java index 63143c16..a9f403db 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java @@ -86,7 +86,7 @@ public class EsiaAuthService { @Autowired private JwtTokenService jwtTokenService; @Autowired - private ReplyingKafkaService replyingKafkaService; + private ReplyingKafkaService replyingKafkaService; @Autowired private OkopfService okopfService; @Autowired diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java index 8a5cee16..8d5aabe5 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java @@ -22,4 +22,6 @@ public interface UlDataService { String getAllUserRoles(String accessToken); EsiaHeader readHeader(String accessToken); + + MchdInfoModel getMchdInfoModel(String guid, String accessToken); } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java index de584e9f..d4214a6b 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java @@ -16,11 +16,11 @@ import ru.micord.ervu.security.esia.config.EsiaConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; -import ru.micord.ervu.security.esia.exception.EsiaException; import ru.micord.ervu.security.esia.model.BrhsModel; import ru.micord.ervu.security.esia.model.EmployeeModel; import ru.micord.ervu.security.esia.model.EsiaAccessToken; import ru.micord.ervu.security.esia.model.EsiaHeader; +import ru.micord.ervu.security.esia.model.MchdInfoModel; import ru.micord.ervu.security.esia.model.OrganizationModel; import ru.micord.ervu.security.esia.model.PersonModel; @@ -282,4 +282,25 @@ public class UlDataServiceImpl implements UlDataService { throw new EsiaException(e); } } + + public MchdInfoModel getMchdInfoModel(String guid, String accessToken) { + try { + String url = esiaConfig.getEsiaBaseUri() + "/poa-registry/api/public/v1/poa/" + guid; + HttpRequest getReq = HttpRequest.newBuilder(URI.create(url)) + .header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded") + .header("Authorization", "Bearer ".concat(accessToken)) + .GET() + .timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout())) + .build(); + HttpResponse getResp = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout())) + .build() + .send(getReq, HttpResponse.BodyHandlers.ofString()); + errorHandler(getResp); + return objectMapper.readValue(getResp.body(), MchdInfoModel.class); + } + catch (Exception e) { + throw new EsiaException(e); + } + } } diff --git a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java index c5688d16..b02c9e2c 100644 --- a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java @@ -41,8 +41,6 @@ public class InteractionServiceImpl implements InteractionService { .set(INTERACTION_LOG.SENT_DATE, timestamp) .set(INTERACTION_LOG.SENDER, sender) .set(INTERACTION_LOG.FILE_NAME, fileName) - .set(INTERACTION_LOG.RECORDS_SENT, 0) - .set(INTERACTION_LOG.RECORDS_ACCEPTED, 0) .set(INTERACTION_LOG.ERVU_ID, ervuId) .execute(); } diff --git a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java new file mode 100644 index 00000000..05ed5e7d --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java @@ -0,0 +1,48 @@ +package ru.micord.ervu.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.common.utils.Bytes; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import ru.micord.ervu.exception.ExportException; +import ru.micord.ervu.kafka.service.ReplyingKafkaService; +import ru.micord.ervu.model.ValidateExportRequest; +import ru.micord.ervu.model.ValidateExportResponse; + +/** + * @author Adel Kalimullin + */ +@Service +public class ValidationFileService { + private final ReplyingKafkaService replyingKafkaService; + private final ObjectMapper objectMapper; + private final String validateExportRequestTopic; + private final String validateExportReplyTopic; + + public ValidationFileService( + ReplyingKafkaService replyingKafkaService, + ObjectMapper objectMapper, + @Value("${ervu.kafka.validate.export.request}") String validateExportRequestTopic, + @Value("${ervu.kafka.validate.export.response}") String validateExportReplyTopic) { + this.replyingKafkaService = replyingKafkaService; + this.objectMapper = objectMapper; + this.validateExportRequestTopic = validateExportRequestTopic; + this.validateExportReplyTopic = validateExportReplyTopic; + } + + + public ValidateExportResponse exportFile(String ervuId, String fileId) { + try { + ValidateExportRequest validateExportRequest = new ValidateExportRequest(ervuId, fileId); + + byte[] bytes = replyingKafkaService.sendMessageAndGetReply( + validateExportRequestTopic, validateExportReplyTopic, + objectMapper.writeValueAsString(validateExportRequest) + ).get(); + return new ValidateExportResponse(bytes); + } + catch (Exception e) { + throw new ExportException("Failed to export file: " + e.getMessage(), e); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java index 43cc0983..18edee51 100644 --- a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java +++ b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java @@ -5,6 +5,7 @@ import java.util.Comparator; import java.util.List; import java.util.HashSet; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import javax.servlet.http.Cookie; @@ -25,13 +26,15 @@ import org.springframework.stereotype.Service; import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; +import static ervu.FileConstants.DOCUMENT; + @Service public class JournalInMemoryStaticGridLoadService implements InMemoryStaticGridLoadService { private final JwtTokenService jwtTokenService; private final InteractionService interactionService; - private final ReplyingKafkaService replyingKafkaService; + private final ReplyingKafkaService replyingKafkaService; private final ObjectMapper objectMapper; private final HttpServletRequest request; @@ -44,7 +47,7 @@ public class JournalInMemoryStaticGridLoadService implements public JournalInMemoryStaticGridLoadService(JwtTokenService jwtTokenService, InteractionService interactionService, - ReplyingKafkaService replyingKafkaService, + ReplyingKafkaService replyingKafkaService, ObjectMapper objectMapper, HttpServletRequest request) { this.jwtTokenService = jwtTokenService; this.interactionService = interactionService; @@ -68,17 +71,26 @@ public class JournalInMemoryStaticGridLoadService implements JournalFileDataResponse journalFileDataResponse = objectMapper.readValue(responseJsonString, JournalFileDataResponse.class); ervuJournalList = journalFileDataResponse.getFilesInfo().stream() - .map(JournalDtoMapper::mapToJournalDto) + .flatMap(fileInfo -> fileInfo.getPackFiles().stream() + .filter(fileDetail -> DOCUMENT.equals(fileDetail.getType())) + .map(fileDetail -> JournalDtoMapper.mapToJournalDto(fileInfo, fileDetail)) + ) .toList(); } catch (JsonProcessingException e) { throw new JsonParsingException("Failed to parse JournalFileDataResponse.", e); } + AtomicInteger counter = new AtomicInteger(1); HashSet seenFileIds = new HashSet<>(); return Stream.concat(dbJournalList.stream(), ervuJournalList.stream()) .filter(journal -> seenFileIds.add(journal.getFileId())) - .sorted(Comparator.comparing(JournalDto::getDepartureDateTime)) + .sorted(Comparator.comparing(JournalDto::getDepartureDateTime).reversed()) + .map(journal -> + journal + .setDocumentNumber(counter.getAndIncrement()) + .setHasFailedRows(journal.getRowsError() != null && journal.getRowsError() > 0) + ) .toList(); } diff --git a/backend/src/main/resources/ProtoResponseData.proto b/backend/src/main/resources/ProtoResponseData.proto new file mode 100644 index 00000000..63979db3 --- /dev/null +++ b/backend/src/main/resources/ProtoResponseData.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package rtl.pgs.ervu.lkrp.journal; +import "google/protobuf/timestamp.proto"; + +option java_multiple_files = true; +option java_outer_classname = "LkrpResponse"; +option java_package = "rtl.pgs.ervu.lkrp.journal.proto"; + +message ResponseData { + string fileName = 1; + bytes file = 2; +} \ No newline at end of file diff --git a/backend/src/main/resources/messages/common_errors_messages.properties b/backend/src/main/resources/messages/common_errors_messages.properties index c6fba19d..7f06599b 100644 --- a/backend/src/main/resources/messages/common_errors_messages.properties +++ b/backend/src/main/resources/messages/common_errors_messages.properties @@ -1,4 +1,15 @@ kafka_reply_timeout=Превышено время ожидания ответа от сервера. access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. -crl_certificate_expired=Превышено время ожидания ответа из ЕСИА \ No newline at end of file +crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +cert_trust_revocation=Неизвестен статус отзыва сертификата или одного из сертификатов в цепочке (проблема с CRL) +cert_untrusted_root=Сертификат или цепочка сертификатов основана на ненадежном корневом сертификате +cert_is_not_time_valid=Этот сертификат или один из сертификатов в цепочке сертификатов является недопустимым по времени +file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись +mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом +mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации +mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительских доверенностей недействующая. +mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации +av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом +mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности diff --git a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties index c6fba19d..acca81c5 100644 --- a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties +++ b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties @@ -1,4 +1,15 @@ kafka_reply_timeout=Превышено время ожидания ответа от сервера. access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. -crl_certificate_expired=Превышено время ожидания ответа из ЕСИА \ No newline at end of file +crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +cert_trust_revocation=Неизвестен статус отзыва сертификата или одного из сертификатов в цепочке (проблема с CRL) +cert_untrusted_root=Сертификат или цепочка сертификатов основана на ненадежном корневом сертификате +cert_is_not_time_valid=Этот сертификат или один из сертификатов в цепочке сертификатов является недопустимым по времени +file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись +mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом +mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации +mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительских доверенностей недействующая. +mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации +av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом +mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности \ No newline at end of file diff --git a/config.md b/config.md index 9ad2aa56..ab43a8bf 100644 --- a/config.md +++ b/config.md @@ -106,20 +106,23 @@ ## Топики Kafka -| Наименование | Описание | Пишет | Читает | Имя конфигурации | -| ---------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------- | -------------------------------------------------- | -------------------------------- | -| ervu.organization.request | топик для записи данных об организации, для получения id организации из ЕРВУ | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_ORG_REQUEST_TOPIC | -| ervu.organization.response | топик для чтения id организации из ЕРВУ | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_ORG_REPLY_TOPIC | -| ervu.organization.journal.request | топик для записи запроса для получения данных по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_JOURNAL_REQUEST_TOPIC | -| ervu.organization.journal.response | топик для чтения данных по журналу взаимодействия | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_JOURNAL_REPLY_TOPIC | -| ervu.lkrp.excerpt.request | топик для записи запроса для получения выписки по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_EXCERPT_REQUEST_TOPIC | -| ervu.lkrp.excerpt.response | топик для чтения выписки по журналу взаимодействия. Содержит ссылку на S3 с файлом выписки | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_EXCERPT_REPLY_TOPIC | -| ervu.lkrp.av-fileupload | топик для записи данных по файлу для перекладчика | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_MESSAGE_TOPIC_NAME | -| ervu.lkrp.av-fileupload-status | топик для чтения статусов файла, полученных от перекладчика | ervu-lkrp-av | ervu-lkrp-ul-backend | AV_KAFKA_DOWNLOAD_RESPONSE | -| ervu.lkrp.auth.events | топик для отправки аудита в журнал авторизации | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_AUTHORIZATION_TOPIC | -| ervu.lkrp.action.events | топик для отправки аудита в журнал действий пользователя | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_ACTION_TOPIC | +| Наименование | Описание | Пишет | Читает | Имя конфигурации | +|------------------------------------|--------------------------------------------------------------------------------------------|----------------------------|---------------------------------------------------|----------------------------------| +| ervu.organization.request | топик для записи данных об организации, для получения id организации из ЕРВУ | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_ORG_REQUEST_TOPIC | +| ervu.organization.response | топик для чтения id организации из ЕРВУ | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_ORG_REPLY_TOPIC | +| ervu.organization.journal.request | топик для записи запроса для получения данных по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_JOURNAL_REQUEST_TOPIC | +| ervu.organization.journal.response | топик для чтения данных по журналу взаимодействия | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_JOURNAL_REPLY_TOPIC | +| ervu.lkrp.excerpt.request | топик для записи запроса для получения выписки по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_EXCERPT_REQUEST_TOPIC | +| ervu.lkrp.excerpt.response | топик для чтения выписки по журналу взаимодействия. Содержит ссылку на S3 с файлом выписки | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_EXCERPT_REPLY_TOPIC | +| ervu.lkrp.av-fileupload | топик для записи данных по файлу для перекладчика | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_DOWNLOAD_REQUEST | +| ervu.lkrp.av-fileupload-status | топик для чтения статусов файла, полученных от перекладчика | ervu-lkrp-av | ervu-lkrp-ul-backend | AV_KAFKA_DOWNLOAD_RESPONSE | +| ervu.lkrp.auth.events | топик для отправки аудита в журнал авторизации | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_AUTHORIZATION_TOPIC | +| ervu.lkrp.action.events | топик для отправки аудита в журнал действий пользователя | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_ACTION_TOPIC | | ervu.lkrp.download.request | топик для отправки аудита в журнал обмена файлами | ervu-lkrp-ul-backend | ervu-organization-registry, ervu-validate-recruits | AUDIT_KAFKA_FILE_UPLOAD_TOPIC | -| ervu.lkrp.import.file | топик для отправки аудита в журнал загрузки ЮЛ и ФЛ | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC | +| ervu.lkrp.import.file | топик для отправки аудита в журнал загрузки ЮЛ и ФЛ | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC | +| ervu.lkrp.download.request | топик для записи данных по загруженным файлам | ervu-lkrp-ul-backend | ervu-organization-registry, ervu-validate-recruits| ERVU_KAFKA_DOWNLOAD_REQUEST | +| ervu.lkrp.download.response | топик для чтения данных по загруженным файлам после обработки ерву | ervu-validate-recruits | ervu-lkrp-ul-backend | ERVU_KAFKA_DOWNLOAR_RESPONSE | +| ervu.lkrp.av-clear-s3 | топик для отчистки фалов из s3 | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_CLEAR_S3 | ## Прочее diff --git a/config/local.env b/config/local.env index 80a01839..349163db 100644 --- a/config/local.env +++ b/config/local.env @@ -16,7 +16,7 @@ KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule KAFKA_USER=user1 KAFKA_PASS=Blfi9d2OFG -AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload +AV_KAFKA_DOWNLOAD_REQUEST=file-to-upload AV_KAFKA_GROUP_ID=local-ervu-lkrp-ul-backend AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status diff --git a/config/micord.env b/config/micord.env index b7a13457..3664f24a 100644 --- a/config/micord.env +++ b/config/micord.env @@ -13,7 +13,7 @@ KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule KAFKA_USER=user1 KAFKA_PASS=Blfi9d2OFG -AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload +AV_KAFKA_DOWNLOAD_REQUEST=file-to-upload AV_KAFKA_GROUP_ID=1 AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index 74055d41..39ba4353 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -64,7 +64,8 @@ - + + diff --git a/frontend/src/resources/css/components-lkrp.css b/frontend/src/resources/css/components-lkrp.css index dbbb3748..616aa694 100644 --- a/frontend/src/resources/css/components-lkrp.css +++ b/frontend/src/resources/css/components-lkrp.css @@ -306,6 +306,44 @@ background-image: url(../img/svg/pin.svg); } +.download-cell-renderer { + display: flex; + justify-content: center; + align-items: center; +} + +.download-btn { + background: transparent; + border: none; + cursor: pointer; + padding: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.15s ease, opacity 0.15s ease; + min-width: 36px; + min-height: 36px; +} + +.download-btn i { + color: #555; + font-size: 26px; +} + +.download-btn:hover i { + color: #000; +} + +.download-btn:active i { + transform: scale(0.9); +} + +.download-btn:disabled, +.download-btn[disabled] { + opacity: 0.3; + cursor: default; +} + .webbpm.ervu_lkrp_ul .paragraph-group > .vertical-container > * { margin-bottom: 16px; } @@ -818,11 +856,14 @@ margin-bottom: 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload { +.webbpm.ervu_lkrp_ul .modal.show :is(ervu-file-upload, ervu-additional-file-upload) { display: block; margin: var(--indent-mini) 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone { +.webbpm.ervu_lkrp_ul .modal.show :is(ervu-file-upload, ervu-additional-file-upload) ~ button-component:not(.info):not(.link) { + padding-top: 0; +} +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone { display: flex; flex-direction: column; align-items: center; @@ -831,14 +872,14 @@ border: 2px dashed var(--border-light); background: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone .select-file-field-text { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone .select-file-field-text { color: var(--color-light); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone .select-file-btn { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone .select-file-btn { margin-top: var(--indent-mini); outline: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file { display: flex; white-space: nowrap; padding: 12px 16px; @@ -846,7 +887,7 @@ border-radius: 4px; background-color: var(--bg-light); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-name { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-name { position: relative; color: var(--color-text-primary); font-size: var(--size-text-secondary); @@ -855,24 +896,24 @@ 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; left: 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-size { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-size { color: var(--border-light); font-family: 'InterL'; font-size: var(--size-text-secondary); margin-left: 8px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-size::before { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-size::before { position: relative; content: "|"; margin-right: 8px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-delete-btn { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-delete-btn { color: var(--color-link); font-size: var(--size-text-secondary); margin-left: auto; @@ -880,12 +921,9 @@ background-color: transparent; outline: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-delete-btn:is(:hover, :focus, :active) { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-delete-btn:is(:hover, :focus, :active) { color: var(--color-link-hover); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload ~ button-component:not(.info):not(.link) { - padding-top: 0; -} .webbpm.ervu_lkrp_ul .modal.show .modal-content .paragraph-list > .vertical-container > * { position: relative; diff --git a/frontend/src/resources/template/ervu/component/ErvuFileUpload.html b/frontend/src/resources/template/ervu/component/ErvuFileUpload.html index 1dc7cd7c..663ae556 100644 --- a/frontend/src/resources/template/ervu/component/ErvuFileUpload.html +++ b/frontend/src/resources/template/ervu/component/ErvuFileUpload.html @@ -18,7 +18,7 @@ hidden>
- {{item?.file?.name}} + {{item?.file?.name}} {{item?.file?.size/1024/1024 | number: '.2'}} MB
diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts index 4ed140be..e4657e7e 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts @@ -56,12 +56,12 @@ export class ErvuFileUpload extends InputControl { public isFilesListVisible: boolean = true; public isProgressBarVisible: boolean = false; - private fileInputEl: HTMLInputElement; + public fileInputEl: HTMLInputElement; private url: string = '/backend/employee/document'; - private messagesService: MessagesService; - private isUploadErrorOccurred = false; + public messagesService: MessagesService; + public isUploadErrorOccurred = false; private appConfigService: AppConfigService; - private cookieService: CookieService; + public cookieService: CookieService; constructor(el: ElementRef, cd: ChangeDetectorRef) { super(el, cd); @@ -135,7 +135,7 @@ export class ErvuFileUpload extends InputControl { this.cd.markForCheck(); } - private setUploaderMethods() { + public setUploaderMethods() { this.uploader.onBeforeUploadItem = (fileItem: FileItem) => { //refresh headers @@ -221,6 +221,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 } diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts new file mode 100644 index 00000000..78b0988a --- /dev/null +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -0,0 +1,121 @@ +import {NotNull, ObjectRef} from "@webbpm/base-package"; +import {ChangeDetectionStrategy, Component} from "@angular/core"; +import {FileItem} from "ng2-file-upload"; +import {EmployeeInfoFileFormType} from "./EmployeeInfoFileFormType"; +import {TokenConstants} from "../../../modules/security/TokenConstants"; +import {ErvuFileUpload} from "./ErvuFileUpload"; +import {FileLikeObject} from "ng2-file-upload/file-upload/file-like-object.class"; + +@Component({ + moduleId: module.id, + selector: "ervu-additional-file-upload", + templateUrl: "./../../../../../src/resources/template/ervu/component/ErvuFileUpload.html", + changeDetection: ChangeDetectionStrategy.OnPush + }) +export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { + + @ObjectRef() + @NotNull() + public signFileUploadRef: ErvuFileUpload; + @ObjectRef() + @NotNull() + public mchdFileUploadRef: ErvuFileUpload; + + public 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({ + headers: [ + { + name: "X-Employee-Info-File-Form-Type", + value: EmployeeInfoFileFormType[this.formType] + }, + { + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }, + { + name: TokenConstants.CSRF_HEADER_NAME, + value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) + } + ], + additionalParameter: additionalParams + }); + this.fileUploadStartEvent.trigger(); + this.isDropZoneVisible = false; + this.isFilesListVisible = false; + this.isProgressBarVisible = true; + this.cd.markForCheck(); + }; + this.uploader.onErrorItem = (item: FileItem, + response: string) => { + this.fileUploadFailedEvent.trigger(); + this.uploader.cancelAll(); + try { + var error = JSON.parse(response); + error.messages.forEach((errorMessage) => { + this.messagesService.error(errorMessage, error); + }) + } + catch (error) { + this.messagesService.error(`Не удалось отправить следующие файлы: ${item.file.name}` + + ` ${this.uploader.getNotUploadedItems() + .map(notUploadeditem => notUploadeditem.file.name) + .join(', ')}.`); + } + this.fileInputEl.value = null; + this.uploader.clearQueue(); + this.mchdFileUploadRef.uploader.clearQueue(); + this.mchdFileUploadRef.fileInputEl.value = null; + this.signFileUploadRef.uploader.clearQueue(); + this.signFileUploadRef.fileInputEl.value = null; + this.isDropZoneVisible = true; + this.isFilesListVisible = true; + this.isProgressBarVisible = false; + this.isUploadErrorOccurred = true; + this.cd.markForCheck(); + }; + + this.uploader.onCompleteAll = () => { + if (!this.isUploadErrorOccurred) { + this.uploader.clearQueue(); + this.mchdFileUploadRef.uploader.clearQueue(); + this.signFileUploadRef.uploader.clearQueue(); + this.fileUploadEndEvent.trigger(); + this.isProgressBarVisible = false; + this.cd.markForCheck(); + } + }; + + this.uploader.onAfterAddingFile = (fileItem: FileItem) => { + this.fileAddedEvent.trigger(); + } + + this.uploader.onWhenAddingFileFailed = (item: FileLikeObject, filter: any, options: any) => { + switch (filter.name) { + case "fileSize": + this.messagesService.error(`Размер файла ${item.name} превышает предельно допустимый = ${this.maxFileSizeMb} MB`); + break; + case "queueLimit": + this.messagesService.error(`Не удалось добавить файл ${item.name}. ` + + `Достигнуто максимальное количество файлов для загрузки = ${this.maxFilesToUpload}`); + break; + case "extension": + this.messagesService.error(`Файл ${item.name} имеет недопустимое расширение.`); + break; + default: + this.messagesService.error(`Не удалось добавить файл ${item.name}.`); + } + this.fileInputEl.value = null; + this.cd.markForCheck(); + }; + } +} diff --git a/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts b/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts index 99d7fd14..581b33d9 100644 --- a/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts +++ b/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts @@ -1,5 +1,4 @@ import { - Event, GridColumnIdUtils, GridRow, GridRowModelType, @@ -11,17 +10,19 @@ import {ChangeDetectionStrategy, Component} from "@angular/core"; import { ColDef, GridReadyEvent, - FilterChangedEvent, ICellRendererParams, ITooltipParams, ValueFormatterParams, - ValueGetterParams + ValueGetterParams, } from "ag-grid-community"; import {StaticColumnInitializer} from "./StaticColumnInitializer"; -import {InMemoryStaticGridRpcService} from "../../../generated/ru/micord/ervu/service/rpc/InMemoryStaticGridRpcService"; +import { + InMemoryStaticGridRpcService +} from "../../../generated/ru/micord/ervu/service/rpc/InMemoryStaticGridRpcService"; import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn"; -import { FilterService } from "../../service/FilterService"; +import {FilterService} from "../../service/FilterService"; import {AuditConstants, AuditService, FilterInfo} from "../../service/AuditService"; +import {ValidateFileService} from "../../service/ValidateFileService"; @Component({ @@ -34,6 +35,7 @@ export class InMemoryStaticGrid extends GridV2 { private rpcService: InMemoryStaticGridRpcService; private auditService: AuditService; + private validateFileService: ValidateFileService; getRowModelType(): string { return GridRowModelType.CLIENT_SIDE; @@ -42,6 +44,7 @@ export class InMemoryStaticGrid extends GridV2 { protected initGrid() { super.initGrid(); this.auditService = this.injector.get(AuditService); + this.validateFileService = this.injector.get(ValidateFileService); this.rpcService = this.getScript(InMemoryStaticGridRpcService); if (this.rpcService) { this.rpcService.loadData().then(response => { @@ -85,6 +88,10 @@ export class InMemoryStaticGrid extends GridV2 { } } + public downloadFile(fileId : string){ + this.validateFileService.exportFile(fileId); + } + getColumns(): any[] { return this.getScriptsInChildren(GridV2Column) .map(columnV2 => columnV2.getScript(StaticGridColumn)); @@ -125,6 +132,13 @@ export class InMemoryStaticGrid extends GridV2 { return columnComp.valueFormatter.format(params); } } + + colDef.cellRendererParams = { + context : { + parentComponent: this + } + }; + return colDef; } diff --git a/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts b/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts index 19f8c910..620bd559 100644 --- a/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts +++ b/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts @@ -1,6 +1,7 @@ import {FilterType} from "../../../../generated/ru/micord/ervu/property/grid/FilterType"; import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community"; import {SetFilter} from "./SetFilter"; +import {FileAvailableFilterComp} from "./FileAvailableFilterComp"; export class CustomGridColumnFilterUtils { @@ -16,6 +17,8 @@ export class CustomGridColumnFilterUtils { return DateFilter; case FilterType.SET: return SetFilter; + case FilterType.FILE: + return FileAvailableFilterComp; case FilterType.TEXT: default: return TextFilter; diff --git a/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts b/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts new file mode 100644 index 00000000..a1be012b --- /dev/null +++ b/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts @@ -0,0 +1,77 @@ +import {BaseBooleanComboBoxFilter} from "@webbpm/base-package"; +import {IFilterParams, IDoesFilterPassParams, IFilterComp} from "ag-grid-community"; + + +export class FileAvailableFilterComp extends BaseBooleanComboBoxFilter implements IFilterComp { + private filterValue: boolean | undefined = undefined; + private params!: IFilterParams; + + init(params: IFilterParams): void { + this.params = params; + this.createComboBox("ag-combobox-file-filter"); + this.populateComboBoxWithFixedValues(); + + this.comboBox.addEventListener("change", () => this.onComboBoxChanged()); + } + + protected populateComboBoxWithFixedValues(): void { + const options = [ + { label: "Все", value: undefined }, + { label: "Файл присутствует", value: true }, + { label: "Файл отсутствует", value: false } + ]; + + this.comboBox.innerHTML = ""; + options.forEach(({ label }) => { + const option = document.createElement("option"); + option.textContent = label; + this.comboBox.appendChild(option); + }); + } + + protected onComboBoxChanged(): void { + const selectedIndex = this.comboBox.selectedIndex; + + this.filterValue = + selectedIndex === 1 ? true : + selectedIndex === 2 ? false : + undefined; + + this.params.filterChangedCallback(); + } + + doesFilterPass(params: IDoesFilterPassParams): boolean { + const cellValue = params.data[this.params.colDef.field!]; + return this.filterValue === undefined || cellValue === this.filterValue; + } + + isFilterActive(): boolean { + return this.filterValue !== undefined; + } + + getModel(): any { + return this.filterValue === undefined + ? undefined + : { filter: this.filterValue, type: "equals" }; + } + + setModel(model: any): void { + this.filterValue = model ? model.filter : undefined; + + if (this.filterValue === true) { + this.comboBox.selectedIndex = 1; + } + else if (this.filterValue === false) { + this.comboBox.selectedIndex = 2; + } + else { + this.comboBox.selectedIndex = 0; + } + + this.params.filterChangedCallback(); + } + + getGui(): HTMLElement { + return this.eGui; + } +} \ No newline at end of file diff --git a/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts b/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts index 6b13c5a1..0a439e24 100644 --- a/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts +++ b/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts @@ -49,7 +49,7 @@ export class SetFilter implements IFilterComp { private initCheckBox(id: string, value: string, index: number): HTMLInputElement { this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE); - this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value; + (this.eGui.querySelectorAll('.ag-filter-value')[index] as HTMLElement).textContent = value; let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement; checkbox.setAttribute('id', id); checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this)); diff --git a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts new file mode 100644 index 00000000..35198a39 --- /dev/null +++ b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts @@ -0,0 +1,38 @@ +import {ICellRendererParams} from "ag-grid-community"; +import {GridCellValueRenderer} from "@webbpm/base-package"; + +export class FileDownloadCellRenderer implements GridCellValueRenderer { + render(params: ICellRendererParams): HTMLElement { + const container = document.createElement('div'); + container.className = 'download-cell-renderer'; + + const button = document.createElement('button'); + button.className = 'download-btn'; + button.innerHTML = ''; + button.title = params.data.fileName || 'Скачать файл'; + + if (!params.value) { + button.style.display = 'none'; + } + + button.addEventListener('click', () => { + event.stopPropagation(); + + const fileId = params.data.fileId; + if (!fileId) { + return; + } + + const parentComponent = params.context.parentComponent; + if (parentComponent) { + parentComponent.downloadFile(fileId); + } + + button.blur(); + }); + + container.appendChild(button); + return container; + } + +} \ No newline at end of file diff --git a/frontend/src/ts/ervu/service/FilterService.ts b/frontend/src/ts/ervu/service/FilterService.ts index b9a9863d..52f24f84 100644 --- a/frontend/src/ts/ervu/service/FilterService.ts +++ b/frontend/src/ts/ervu/service/FilterService.ts @@ -1,6 +1,7 @@ import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community"; import {SetFilter} from "../component/grid/filter/SetFilter"; import {FilterInfo} from "./AuditService"; +import {FileAvailableFilterComp} from "../component/grid/filter/FileAvailableFilterComp"; export class FilterService { static getFilterData(columnDef: any, agFilter: any): FilterInfo { @@ -15,6 +16,8 @@ export class FilterService { return this.processSetFilter(agFilter); case TextFilter: return this.processTextFilter(agFilter); + case FileAvailableFilterComp: + return this.processFileAvailableFilter(agFilter); default: return; } @@ -63,13 +66,25 @@ export class FilterService { return this.createDualConditionData(agFilter); } + private static processFileAvailableFilter(agFilter: any): FilterInfo { + const displayValue = agFilter.filter + ? "Файл присутствует" + : "Файл отсутствует"; + + return { + conditions: [{ + filterValue: displayValue, + filterType: "equals" + }] + }; + } + private static createSingleConditionData( filterValue: string, filterType: string, endValue?: string ): FilterInfo { return { - conditionOperator: undefined, conditions: [{ filterValue: endValue ? `${filterValue} to ${endValue}` : filterValue, filterType: filterType, diff --git a/frontend/src/ts/ervu/service/ValidateFileService.ts b/frontend/src/ts/ervu/service/ValidateFileService.ts new file mode 100644 index 00000000..91ab4a3b --- /dev/null +++ b/frontend/src/ts/ervu/service/ValidateFileService.ts @@ -0,0 +1,34 @@ +import {Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {MessagesService} from "@webbpm/base-package"; + +@Injectable({ + providedIn: "root" + }) +export class ValidateFileService { + constructor(private httpClient: HttpClient) { + } + + public exportFile(fileId: string) { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + this.httpClient.get('validate/export/' + fileId, { + responseType: 'blob', + headers: { + "Client-Time-Zone": timeZone, + }, + observe: 'response' + }).toPromise() + .then((response) => { + const data = window.URL.createObjectURL(response.body); + const link = document.createElement("a"); + link.href = data; + const contentDisposition = response.headers.get('Content-Disposition'); + const fileNameMatch = contentDisposition.match(/filename\*=?UTF-8''(.+)/i); + link.download = decodeURIComponent(fileNameMatch[1].replace(/\+/g, '%20')); + document.body.appendChild(link); + link.click(); + window.URL.revokeObjectURL(data); + link.remove(); + }); + } +} \ No newline at end of file diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index 792a4c3f..517811de 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -27,6 +27,10 @@ import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFi import {AuthenticationService} from "../security/authentication.service"; import {HomeLandingComponent} from "./component/home-landing.component"; import {AuditService} from "../../ervu/service/AuditService"; +import { + ErvuFileUploadWithAdditionalFiles +} from "../../ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles"; +import {ValidateFileService} from "../../ervu/service/ValidateFileService"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -36,6 +40,7 @@ export const DIRECTIVES = [ forwardRef(() => AccessDeniedComponent), forwardRef(() => AppProgressIndicationComponent), forwardRef(() => ErvuFileUpload), + forwardRef(() => ErvuFileUploadWithAdditionalFiles), forwardRef(() => ErvuDownloadFileButton), forwardRef(() => InMemoryStaticGrid), forwardRef(() => HomeLandingComponent), @@ -66,7 +71,7 @@ export function checkAuthentication(authService: AuthenticationService): () => P DIRECTIVES ], providers: [ - AuthenticationService, AuditService, + AuthenticationService, AuditService, ValidateFileService, { provide: APP_INITIALIZER, useFactory: checkAuthentication, diff --git a/pom.xml b/pom.xml index 95147c95..5ba24ef6 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ 2.9.2 3.192.26 72000 + 1.6.2 @@ -270,6 +271,16 @@ 1.1.10.7 runtime + + com.google.protobuf + protobuf-java + 4.28.3 + + + com.google.protobuf + protobuf-java-util + 4.28.3 + org.slf4j slf4j-api @@ -300,6 +311,11 @@ sardine 5.12 + + org.apache.httpcomponents + httpmime + 4.5.14 + @@ -387,6 +403,11 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + diff --git a/resources/src/main/resources/business-model/Журнал взаимодействия.page b/resources/src/main/resources/business-model/Журнал взаимодействия.page index 714d98cf..57f0567b 100644 --- a/resources/src/main/resources/business-model/Журнал взаимодействия.page +++ b/resources/src/main/resources/business-model/Журнал взаимодействия.page @@ -982,11 +982,106 @@ + + c556264f-221b-4af8-9e64-f380a67c41ec + 7094b2c2-60c7-43d0-84d7-3adbd3c77fe9 + №П/П + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"№П/П" + + + + displayPopup + +true + + + + field + + + + column + + "documentNumber" + + + + filterType + + "NUMBER" + + + + type + + "java.lang.Number" + + + + + + + filter + +true + + + + pinned + +"LEFT" + + + + sortable + +true + + + + width + +50 + + + + widthFixed + +null + + + + + c556264f-221b-4af8-9e64-f380a67c41ec 9b895ce1-494f-4f2e-b87b-78d019fc3760 Дата и время направления false + false false @@ -1064,7 +1159,7 @@ filter -true +null @@ -1118,6 +1213,12 @@ true + + disableHiding + +null + + displayName @@ -1489,7 +1590,7 @@ column - "filesSentCount" + "rowsCount" @@ -1539,6 +1640,7 @@ 4c070d5d-cac7-4cc4-8ee4-e9a4b9b289a5 Записей принято false + false false @@ -1577,7 +1679,7 @@ column - "acceptedFilesCount" + "rowsSuccess" @@ -1616,6 +1718,293 @@ widthFixed +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 7189ce80-0090-44c9-a8ba-b3860b1fefb8 + Записей не принято + false + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"Записей не принято" + + + + displayPopup + +true + + + + field + + + + column + + "rowsError" + + + + filterType + + "NUMBER" + + + + type + + "java.lang.Number" + + + + + + + filter + +true + + + + sortable + +true + + + + width + +80 + + + + widthFixed + +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 33376e71-d633-464c-bd0a-da95caeae54b + Загрузка файла + false + false + false + + + + floatingFilter + +false + + + + renderer + + + FileDownloadCellRenderer + ervu.component.grid.renderer + + + + + + + + + autoHeight + +true + + + + displayName + +"Описание ошибки" + + + + displayPopup + +true + + + + field + + + + column + + "hasFailedRows" + + + + filterType + + "FILE" + + + + type + + "java.lang.Boolean" + + + + + + + filter + +true + + + + hidden + +null + + + + sortable + +true + + + + width + +80 + + + + widthFixed + +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 438864af-918d-4264-bc37-192f94263673 + id Файла(скрытое) + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"id файла" + + + + displayPopup + +true + + + + field + + + + column + + "fileId" + + + + filterType + + "TEXT" + + + + type + + "java.lang.String" + + + + + + + filter + +true + + + + hidden + +true + + + + sortable + +true + + + + width + +null + + + + widthFixed + null diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index c3241ec3..eb8f7f58 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -221,6 +221,7 @@ 3e78f422-3db3-45b9-b531-f4aec5314dab Группа полей true + false false @@ -251,6 +252,7 @@ c9898352-a317-4117-bfcc-28b5c4d9a0d1 Горизонтальный контейнер true + false false @@ -355,6 +357,7 @@ 1be5e2cd-f42e-40c6-971c-e92f997a7139 Горизонтальный контейнер true + false false @@ -444,6 +447,7 @@ a6c5ce4e-9773-45ee-9153-9cd79ef13a97 Hbox_9 true + false false @@ -623,6 +627,7 @@ ea3e7a6d-e69c-4ef0-8a80-d8fd038a8c1e Hbox_9_ true + false false @@ -780,6 +785,7 @@ 8a23f760-b1fc-436a-8bd5-f87664c7a50f Hbox true + false false @@ -904,6 +910,12 @@ false + + elseActions + + + + eventRefs @@ -1078,7 +1090,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -1113,19 +1125,30 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} method - "reset" + "setVisible" value - null + + + staticValue + + + boolean + + + true + + + @@ -1194,6 +1217,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bc8596fe-1e46-4cf9-b306-43498f61909c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"7f70dc30-8116-47da-ad8d-01cec26a60d7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"88c07c71-e76e-4ff6-a24d-b838bc495737","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -1241,7 +1439,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -1359,38 +1557,7 @@ 529140ee-17b6-44e9-8f2d-a097b4bc044d Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении)" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -1480,6 +1647,857 @@ false + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 7ea732e4-71c7-4325-8569-462acbea89b8 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 7ea732e4-71c7-4325-8569-462acbea89b8 + Вертикальный контейнер + true + false + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 529140ee-17b6-44e9-8f2d-a097b4bc044d + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении)" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 7d338f47-6d12-4040-ba18-f31f520dce8d + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 4333be98-584a-4d30-9427-4d4f7a8b7f7e + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b8373653-133f-43cd-a8c2-ba83f232d379 + Вертикальный контейнер + true + false + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bc8596fe-1e46-4cf9-b306-43498f61909c + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша откреплённая электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 409c0323-011e-4416-a631-f8f852299e1f + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 7f70dc30-8116-47da-ad8d-01cec26a60d7 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c4d501b1-7565-45c3-8722-d624126f6cfb + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c4d501b1-7565-45c3-8722-d624126f6cfb + Вертикальный контейнер + true + false + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 81d2a10f-eb67-4002-9cbd-1e67b87888ef + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + e958f2ee-e112-4bef-9c8a-40e2f8278ca9 + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 88c07c71-e76e-4ff6-a24d-b838bc495737 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 5751d822-0921-4109-9004-ba5d948cf2c4 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 1dc25461-a60e-456b-93cb-cc0f28dc347b + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 9b446462-16e5-4241-b409-0287dea92b3a + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b758e782-c557-445b-a0da-fcc5a79a36e9 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + ddb464de-7f23-40dd-88bb-a06578ecf0e3 + Vbox + true + true + 5ebd2885-0972-4e51-8376-1fa66aed9a90 @@ -1493,73 +2511,35 @@ 7d338f47-6d12-4040-ba18-f31f520dce8d FileUploadV2 false - false - - - - collectible - - false - - - - cssClasses - - - - "btn-main" - - - - - - extensionFilter - - - - "csv" - - - - - - formType - - "FORM_9" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 4333be98-584a-4d30-9427-4d4f7a8b7f7e + Текст + false + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bc8596fe-1e46-4cf9-b306-43498f61909c + Текст + false + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 409c0323-011e-4416-a631-f8f852299e1f + FileUploadV2 + false + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 7f70dc30-8116-47da-ad8d-01cec26a60d7 + Текст + false + true 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -1584,7 +2564,25 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -1605,6 +2603,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -1684,7 +2784,25 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -1705,6 +2823,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_EMPTY" + + + + + + + + @@ -1712,7 +2932,7 @@ logicalOperation - null + "OR" @@ -1827,7 +3047,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -1850,39 +3070,6 @@ - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - 4333be98-584a-4d30-9427-4d4f7a8b7f7e - Текст - false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - - fd7e47b9-dce1-4d14-9f3a-580c79f59579 9d068751-8bbe-4fa0-b4b5-fa8e395fa019 @@ -1939,7 +3126,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2176,6 +3363,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bc8596fe-1e46-4cf9-b306-43498f61909c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"7f70dc30-8116-47da-ad8d-01cec26a60d7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"88c07c71-e76e-4ff6-a24d-b838bc495737","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -2205,7 +3637,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2287,6 +3719,8 @@ + + @@ -2312,7 +3746,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2394,6 +3828,8 @@ + + @@ -2416,6 +3852,7 @@ 9138d81a-f635-42f6-915c-b3c7be4e2160 Группа полей true + false false @@ -2609,6 +4046,7 @@ be4863fe-d25f-4965-ae52-8552ab5e8cf9 Hbox_2 true + false false @@ -2792,6 +4230,7 @@ 52b1c7dd-7763-4f50-84ed-0471a83dd0b4 Hbox_2_ true + false false @@ -2961,6 +4400,7 @@ dd3fd6ce-3f62-436c-b641-522042f189ff Hbox true + false false @@ -3210,7 +4650,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -3245,7 +4685,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -3326,6 +4766,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bee4c3f7-05b0-4b21-81bb-48249a1bdfdb","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"77096468-9f51-4e2c-93a8-97a38d89570e","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"c22b9906-4d5e-48df-b084-f0ac331de04c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -3373,7 +4988,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -3490,38 +5105,7 @@ c12d155e-947d-41b6-aef0-23080fdc82c0 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Изменения сведений сотрудников, необходимых для ведения воинского учета" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -3612,6 +5196,854 @@ + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 998f235b-9d72-4f87-950b-1ec90e3d8b90 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c12d155e-947d-41b6-aef0-23080fdc82c0 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Изменения сведений сотрудников, необходимых для ведения воинского учета" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f8c09ec6-d52a-4398-a40e-a237f4305b88 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 0970e79a-7130-4c23-bdc8-13f8554daa59 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 6345d7cc-c606-4195-acc2-bc9cfcd300ae + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bee4c3f7-05b0-4b21-81bb-48249a1bdfdb + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша откреплённая электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + a8e76694-830b-4f22-9a5b-bb0efe0d7745 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 77096468-9f51-4e2c-93a8-97a38d89570e + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4234108a-2b52-4b6f-be54-75590266b711 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + d320bfdf-0416-43da-bed8-a4a2131869d7 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4234108a-2b52-4b6f-be54-75590266b711 + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + bb63fe78-e531-4e67-921a-91eff8cd6406 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 2b87f279-a5eb-445f-b96e-4d12b6217126 + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c22b9906-4d5e-48df-b084-f0ac331de04c + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 871b2ab1-fd6a-41fc-97a7-c60052da8272 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 0f616bc4-9148-4d3e-b374-269a91ed54f2 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + f6e4a42a-020e-4b2a-b6e7-6f48024fb438 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 77c7fa81-93a3-49fb-8263-696fd3acda92 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c965e7ee-ddce-446c-a8cd-7264f9bbc2fd + Vbox + true + true + + 5ebd2885-0972-4e51-8376-1fa66aed9a90 e4d86cdd-b219-46e8-a59f-3c9b38108de4 @@ -3624,73 +6056,7 @@ ba4ed1be-acc7-456d-aec2-e29d56f05113 FileUploadV2 false - false - - - - collectible - - false - - - - cssClasses - - - - "btn-main" - - - - - - extensionFilter - - - - "csv" - - - - - - formType - - "FORM_2" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + true 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -3715,7 +6081,25 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -3736,6 +6120,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -3743,7 +6229,7 @@ logicalOperation - null + "AND" @@ -3815,7 +6301,25 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -3958,7 +6462,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -3986,33 +6490,7 @@ 0970e79a-7130-4c23-bdc8-13f8554daa59 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -4070,7 +6548,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -4307,6 +6785,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bee4c3f7-05b0-4b21-81bb-48249a1bdfdb","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"77096468-9f51-4e2c-93a8-97a38d89570e","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"c22b9906-4d5e-48df-b084-f0ac331de04c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -4336,7 +7059,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -4443,7 +7166,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -4547,6 +7270,7 @@ ae731885-3bdd-433d-a29c-37d5811585a7 Группа полей true + false false @@ -4740,6 +7464,7 @@ 47a51ceb-a4db-4caf-a234-4389e2456f66 Hbox_10 true + false false @@ -4916,6 +7641,7 @@ fb8317a8-f9ed-4da3-8018-be3d8efbc469 Hbox_10_ true + false false @@ -5110,6 +7836,12 @@ false + + elseActions + + + + eventRefs @@ -5284,7 +8016,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5319,7 +8051,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5400,6 +8132,184 @@ false + + + + + + + + + + + + + + + behavior + + {"objectId":"9b35b57e-d173-4f38-a1b3-7caf32b601f4","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"c3389547-2857-4464-8d11-87646677e390","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"95fb07cd-5071-412e-8c70-19fbbcf1f70f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -5447,7 +8357,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -5564,38 +8474,7 @@ faef9178-0311-4a71-957b-54bcd5221517 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -5698,73 +8577,862 @@ 623c8ded-d64e-40b4-b1bb-2db70083ae4f FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + ce89da7c-85a0-4670-870b-f6ad52d65591 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + faef9178-0311-4a71-957b-54bcd5221517 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 3d0d0314-ea5f-4974-a92d-32c11ce90861 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 87df2042-bf8e-4182-9abc-34a7b2ec8165 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 276d7e73-eba9-4859-942f-af19f7ff535f + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 276d7e73-eba9-4859-942f-af19f7ff535f + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 9b35b57e-d173-4f38-a1b3-7caf32b601f4 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша откреплённая электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + b7b6470e-64df-4930-9522-c8a5f01ab493 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c3389547-2857-4464-8d11-87646677e390 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + fe37be88-aca5-4191-b5e1-a914a8603965 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + bdf5ede5-8183-4e07-9f5b-a49dc03978bd + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 137c8ee8-cee3-474c-8883-733aaa7ed95e + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 6485951d-c7ca-4df8-835f-6d326215fb99 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_10" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + f8953a9f-71f4-4095-873c-f924960167bb + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 95fb07cd-5071-412e-8c70-19fbbcf1f70f + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + adbccde8-fa29-4c5f-961e-a042a90d5d12 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 51b39385-98cf-4890-8a14-96e371027d05 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 71bc3168-e362-4cd1-8c0f-2df719d8f3fe + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + e6ff2b0f-a6a1-4ab2-aefe-6ba208808eb3 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 56a78b9f-9f5f-4d7c-85a4-e71860886d5a + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -5789,7 +9457,25 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -5810,6 +9496,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -5889,7 +9677,25 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -6032,7 +9838,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -6060,33 +9866,7 @@ 87df2042-bf8e-4182-9abc-34a7b2ec8165 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -6144,7 +9924,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -6381,6 +10161,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"9b35b57e-d173-4f38-a1b3-7caf32b601f4","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"c3389547-2857-4464-8d11-87646677e390","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"95fb07cd-5071-412e-8c70-19fbbcf1f70f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -6410,7 +10435,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -6517,7 +10542,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -6607,6 +10632,13 @@ + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + b59b46f5-0b45-4a0e-99d9-1eed7060be97 + Диалог - выбор файла и отправка + true + true + d7d54cfb-26b5-4dba-b56f-b6247183c24d @@ -6674,6 +10706,7 @@ 7057bbcb-cff2-4e31-812d-ba7e043a4bcc Группа полей true + false false @@ -6867,6 +10900,7 @@ 87d2523e-91a0-49ef-9f87-2adc4ff1165a Hbox_11 true + false false @@ -7043,6 +11077,7 @@ a072a677-36d0-4616-a0bd-3541ebae45c8 Hbox_11_ true + false false @@ -7462,7 +11497,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -7497,7 +11532,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -7578,6 +11613,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"e6baac95-674d-4833-8eaa-142ed5ed0f02","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"52a47dd7-0448-4934-aba0-72c95b93b091","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"780dba1a-b9a9-4a98-9bf0-40dc91577fb7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -7625,7 +11835,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -7742,38 +11952,7 @@ e901dbfc-6ae7-4e81-8702-3a99005376f2 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Ежегодное предоставление списка граждан мужского пола, подлежащих постановке на воинский учет в следующем году по достижении 17 - летнего возраста" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -7876,73 +12055,862 @@ 9c49eea1-7d04-4d99-b213-59aa51499acc FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 36ed9134-7e97-45ea-8215-261f65e69077 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + e901dbfc-6ae7-4e81-8702-3a99005376f2 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ежегодное предоставление списка граждан мужского пола, подлежащих постановке на воинский учет в следующем году по достижении 17 - летнего возраста" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + d202441d-c2ae-4e34-970e-78166e8c4ea9 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + d4f96505-0b2a-4e7b-87c6-e79f1987da66 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4c2623d7-445f-4d4c-9c1a-98e033e206fb + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 40e1b3d6-57ff-4ef7-b93f-72e0c2661b70 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + e6baac95-674d-4833-8eaa-142ed5ed0f02 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша откреплённая электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 52a47dd7-0448-4934-aba0-72c95b93b091 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4b296222-446c-45d5-8a74-019f505c9e7e + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 77cd6896-3da8-47f7-8ce3-c08a5e42b959 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + cae94ea0-75e1-408a-8ad7-34a07b52a381 + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 3f217825-2344-4196-bd31-cf0b27c887a4 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_11" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 17c7b00e-9ea7-47e8-a841-3c49fc24123b + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 780dba1a-b9a9-4a98-9bf0-40dc91577fb7 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + a29b15a1-79b1-4dfa-ac78-223742ac2698 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f47e3ad9-6065-41a7-862d-991c41f1bdfa + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 97a5367f-cc24-4d04-87c5-efaa5d9b7d0b + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + dc182bde-6e42-4ddf-b83d-a5ca05a99cbd + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + be915f17-da79-45f3-afe3-0fa6cac060f4 + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -7967,7 +12935,25 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -7988,6 +12974,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -8067,7 +13155,25 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -8210,7 +13316,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8238,33 +13344,7 @@ d4f96505-0b2a-4e7b-87c6-e79f1987da66 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -8322,7 +13402,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8559,6 +13639,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"e6baac95-674d-4833-8eaa-142ed5ed0f02","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"52a47dd7-0448-4934-aba0-72c95b93b091","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"780dba1a-b9a9-4a98-9bf0-40dc91577fb7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -8576,6 +13901,7 @@ d31780d3-6bdf-4fea-b297-5405f122b0ad Обработка событий - end file upload false + false false @@ -8588,7 +13914,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8683,6 +14009,7 @@ 359f50fe-e079-435c-8844-efcf652c9313 Обработка событий - failed file upload false + false false @@ -8695,7 +14022,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8799,6 +14126,7 @@ 991237d3-8cb9-48af-8501-030a3c8c6cfc Группа полей true + false false @@ -8829,6 +14157,7 @@ 754d7c80-d6f6-4d7f-a902-f5635d5dd60e Горизонтальный контейнер true + false false @@ -9121,6 +14450,7 @@ 025b7644-c658-49b2-a740-188668b8b81e Hbox_12 true + false false @@ -9297,6 +14627,7 @@ 7be4de32-b967-4629-9801-425457e4b9c5 Hbox_12_ true + false false @@ -9445,7 +14776,6 @@ fafbc151-0d60-4e80-90d1-501d426dadc9 Hbox true - false false @@ -9470,6 +14800,7 @@ f6310258-ec19-4d13-b04d-cfc09d6b5e77 Кнопка 5 false + false false @@ -9716,7 +15047,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -9751,7 +15082,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -9839,6 +15170,182 @@ + + + + + behavior + + {"objectId":"3db79e5b-7bea-4043-ab90-1080983f89d3","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"42e0e31c-1fc1-415f-9f4c-64fbd2ba0889","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"da4bcc36-03cf-494d-9757-c3fc7486ef2f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + @@ -9879,7 +15386,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -9996,38 +15503,7 @@ d60694e9-0d17-4e07-ad1d-ad9871e3deaa Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -10130,73 +15606,862 @@ 8f61be32-7f6c-40e3-afba-ad802f48a040 FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + f095d2a8-2f04-4aca-9fe0-14cda3343bb0 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + d60694e9-0d17-4e07-ad1d-ad9871e3deaa + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 6f18b446-eb34-4894-92c8-6654e0380ef5 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 159c7f1e-c71c-4da1-bf51-18550aa8baa4 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b0903742-cbcd-4593-b98b-6055b71bb9ca + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 64b7e42e-3501-4f93-8933-dfcbc75cb245 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 3db79e5b-7bea-4043-ab90-1080983f89d3 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша откреплённая электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + d3bbecf6-acf6-468b-bb67-4b5549c44a58 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 42e0e31c-1fc1-415f-9f4c-64fbd2ba0889 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 47a8d746-1dc3-4237-ac8a-f395bed05493 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + a8c8be34-345b-42b4-a29a-499096a3e5af + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 37859c01-1ec5-4557-8d56-31cf2702abda + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + a8957fcd-8c8a-403c-b45f-2b8632bdf509 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_12" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 3808bc3e-cddf-4a55-b324-f4fe469e4938 + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + da4bcc36-03cf-494d-9757-c3fc7486ef2f + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 085f831f-4df9-444b-b7fc-79b96e7f1f67 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f0d9399b-fb2f-4874-a307-2006e7758f44 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 14da9d8b-7687-4f09-a423-626db4f3e749 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 080a0c2f-b66c-4987-9cf7-80beb26edd1e + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 51813a15-4d66-4b61-b522-e9e24b65b972 + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -10221,7 +16486,25 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -10242,6 +16525,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -10303,6 +16688,7 @@ f897626f-4c10-4648-90fe-51b6f9b31fe9 Обработка событий - файл удален false + false false @@ -10321,7 +16707,25 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -10464,7 +16868,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -10492,33 +16896,7 @@ 159c7f1e-c71c-4da1-bf51-18550aa8baa4 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -10564,6 +16942,7 @@ 09000d10-0142-4051-9ab8-0df98bd5dafa Обработка событий - start file upload false + false false @@ -10576,7 +16955,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -10813,6 +17192,252 @@ false + + + + + + + + + + + + behavior + + {"objectId":"3db79e5b-7bea-4043-ab90-1080983f89d3","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"42e0e31c-1fc1-415f-9f4c-64fbd2ba0889","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + + behavior + + {"objectId":"da4bcc36-03cf-494d-9757-c3fc7486ef2f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -10830,6 +17455,7 @@ e80f8dec-8222-4b94-bc94-e8b2119dfca1 Обработка событий - end file upload false + false false @@ -10842,7 +17468,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -10937,6 +17563,7 @@ 6745088f-5c87-42e6-bc87-41efba248455 Обработка событий - failed file upload false + false false @@ -10949,7 +17576,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"}