diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 052feb17..6b8d5d50 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -218,10 +218,6 @@ public class ApplicationEvaluationDao { setEvaluationDocResponse(response, allDocs); setApplicationDetails(response, entity); setRejectedDocuments(applicationEntity, response); - response.setApplicationCompanyDocuments( - companyDocumentDao.listApplicationCompanyDocumentsForEvaluation( - entity.getApplicationId(), applicationEntity.getCompanyId())); - return response; } @@ -2413,7 +2409,6 @@ public class ApplicationEvaluationDao { response.setAmountAccepted(applicationEvaluationResponse.getAmountAccepted()); response.setDateAccepted(applicationEvaluationResponse.getDateAccepted()); response.setDateRejected(applicationEvaluationResponse.getDateRejected()); - response.setApplicationCompanyDocuments(applicationEvaluationResponse.getApplicationCompanyDocuments()); return response; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDocumentDao.java b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDocumentDao.java index ac92bf04..c8e87f88 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/CompanyDocumentDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/CompanyDocumentDao.java @@ -3,6 +3,7 @@ package net.gepafin.tendermanagement.dao; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.CopyObjectRequest; +import com.fasterxml.jackson.core.type.TypeReference; import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; @@ -13,11 +14,14 @@ import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.CompanyDocumentRequest; +import net.gepafin.tendermanagement.model.request.EvaluationDocumentRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.DocumentCategoryResponse; import net.gepafin.tendermanagement.model.response.CompanyDocumentResponseBean; import net.gepafin.tendermanagement.model.response.DocumentResponseBean; import net.gepafin.tendermanagement.model.response.UploadFileOnAmazonS3Response; +import net.gepafin.tendermanagement.model.util.NanoIdUtils; +import net.gepafin.tendermanagement.repositories.ApplicationEvaluationRepository; import net.gepafin.tendermanagement.repositories.CompanyDocumentRepository; import net.gepafin.tendermanagement.repositories.DocumentRepository; import net.gepafin.tendermanagement.repositories.UserWithCompanyRepository; @@ -38,7 +42,10 @@ import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import java.net.URL; +import java.security.SecureRandom; import java.time.LocalDateTime; +import org.apache.commons.lang3.StringUtils; + import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -98,13 +105,16 @@ public class CompanyDocumentDao { @Autowired private UserWithCompanyRepository userWithCompanyRepository; + @Autowired + private ApplicationEvaluationRepository applicationEvaluationRepository; + /** * Instructor uploads a company document tied to an application. Files are stored under the same S3 layout as * {@link CompanyDocumentTypeEnum#COMPANY_DOCUMENT}; persisted row type remains {@link CompanyDocumentTypeEnum#APPLICATION_DOCUMENT}. */ public List uploadInstructorCompanyDocumentToApplication(Long userId, List files, Long companyId, Long applicationId, Long documentCategoryId, LocalDateTime expirationDate, String name) { log.info("Instructor upload company document to application. userId={}, companyId={}, applicationId={}", userId, companyId, applicationId); - applicationService.validateApplicationWithCompany(applicationId, companyId); + ApplicationEntity application = applicationService.validateApplicationWithCompany(applicationId, companyId); DocumentCategoryEntity categoryEntity = categoryDao.validateCategory(documentCategoryId); Optional userWithCompanyOpt = userWithCompanyRepository.findByUserIdAndCompanyIdAndIsDeletedFalse(userId, companyId); @@ -142,6 +152,9 @@ public class CompanyDocumentDao { companyDocumentRepository.saveAll(companyDocumentEntities); companyDocumentEntities.forEach(entity -> loggingUtil.addVersionHistory( VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build())); + + copyInstructorCompanyDocumentsToEvaluationDocuments(application, companyDocumentEntities, userId); + return companyDocumentEntities.stream() .map(this::convertToCompanyDocumentResponseBean) .collect(Collectors.toList()); @@ -373,6 +386,115 @@ public class CompanyDocumentDao { return responseBean; } + /** + * For instructor uploads: copies each saved company document into the evaluation S3 folder, persists + * {@link DocumentEntity} rows (source EVALUATION), and merges entries into the existing {@code EVALUATION_DOCUMENT} + * column ({@link ApplicationEvaluationEntity#getEvaluationDocument()} JSON array of {@link EvaluationDocumentRequest}), + * appending to any data already stored there. + */ + private void copyInstructorCompanyDocumentsToEvaluationDocuments(ApplicationEntity application, + List instructorCompanyDocuments, Long userId) { + if (instructorCompanyDocuments == null || instructorCompanyDocuments.isEmpty()) { + return; + } + Long evaluationId = application.getApplicationEvaluationId(); + if (evaluationId == null || evaluationId <= 0) { + log.debug("Skip evaluation document copy for applicationId={}: no applicationEvaluationId set", application.getId()); + return; + } + Optional evaluationOpt = applicationEvaluationRepository.findByIdAndIsDeletedFalse(evaluationId); + if (evaluationOpt.isEmpty()) { + log.warn("Skip evaluation document copy: applicationEvaluationId={} not found or deleted", evaluationId); + return; + } + + Long callId = application.getCall().getId(); + Long applicationId = application.getId(); + String evaluationFolderPath = s3ConfigBean.generateDocumentPath( + DocumentSourceTypeEnum.EVALUATION, callId, applicationId, evaluationId, 0L, 0L); + + List newEntries = new ArrayList<>(); + for (CompanyDocumentEntity companyDoc : instructorCompanyDocuments) { + DocumentEntity saved = persistInstructorCompanyDocumentAsEvaluationDocument( + companyDoc, evaluationFolderPath, evaluationId, userId); + if (saved.getId() == null) { + continue; + } + EvaluationDocumentRequest entry = new EvaluationDocumentRequest(); + SecureRandom random = new SecureRandom(); + + String key = NanoIdUtils.randomNanoId( + random, + NanoIdUtils.DEFAULT_ALPHABET, + 10 + ); + entry.setFieldId(key); + entry.setNameValue(instructorDocumentDisplayName(companyDoc)); + entry.setFileValue(String.valueOf(saved.getId())); + entry.setValid(null); + newEntries.add(entry); + } + if (newEntries.isEmpty()) { + return; + } + + ApplicationEvaluationEntity evaluation = evaluationOpt.get(); + List merged = new ArrayList<>(parseEvaluationDocumentJson(evaluation.getEvaluationDocument())); + merged.addAll(newEntries); + evaluation.setEvaluationDocument(Utils.convertObjectToJson(merged)); + applicationEvaluationRepository.save(evaluation); + } + + private static List parseEvaluationDocumentJson(String json) { + if (StringUtils.isBlank(json)) { + return new ArrayList<>(); + } + List parsed = Utils.convertJsonToList(json, + new TypeReference>() {}); + return parsed != null ? new ArrayList<>(parsed) : new ArrayList<>(); + } + + + private static String instructorDocumentDisplayName(CompanyDocumentEntity companyDoc) { + if (companyDoc == null) { + return ""; + } + if (StringUtils.isNotBlank(companyDoc.getName())) { + return companyDoc.getName().trim(); + } + return StringUtils.defaultString(companyDoc.getFileName()); + } + + private DocumentEntity persistInstructorCompanyDocumentAsEvaluationDocument(CompanyDocumentEntity companyDocumentEntity, + String evaluationDocumentFolderPath, Long evaluationId, Long userId) { + String companyDocumentPath = companyDocumentEntity.getFilePath(); + log.info("Instructor evaluation copy: companyDocumentId={}, oldPath={}, evaluationFolder={}", + companyDocumentEntity.getId(), companyDocumentPath, evaluationDocumentFolderPath); + + UploadFileOnAmazonS3Response response; + try { + response = amazonS3ServiceImpl.copyFile(companyDocumentEntity.getName(), companyDocumentPath, evaluationDocumentFolderPath); + } catch (Exception e) { + log.error("Error copying instructor company document id={} to evaluation folder: {}", + companyDocumentEntity.getId(), e.getMessage(), e); + throw new CustomValidationException(Status.VALIDATION_ERROR, + Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3)); + } + + DocumentEntity entity = new DocumentEntity(); + entity.setFilePath(response.getFilePath()); + entity.setFileName(response.getFileName()); + entity.setSource(DocumentSourceTypeEnum.EVALUATION.getValue()); + entity.setType(DocumentTypeEnum.DOCUMENT.getValue()); + entity.setSourceId(evaluationId); + entity.setUploadedBy(userId); + documentRepository.save(entity); + + loggingUtil.addVersionHistory( + VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build()); + return entity; + } + public List getAllCompanyDocument(UserEntity user , Long companyId, CompanyDocumentTypeEnum typeEnum){ log.info("Fetching all company documents for Company ID '{}', User ID '{}', Type '{}'", companyId, user.getId(), typeEnum); CompanyEntity companyEntity = companyService.validateCompany(companyId); diff --git a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java index 86788e88..d05f5816 100644 --- a/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java +++ b/src/main/java/net/gepafin/tendermanagement/model/response/ApplicationEvaluationResponse.java @@ -25,7 +25,6 @@ public class ApplicationEvaluationResponse { private List checklist; private List files; private List evaluationDocument; - private List applicationCompanyDocuments; private List amendmentDetails; private LocalDateTime createdDate; private LocalDateTime updatedDate;