From ed856947b5dfbf39cbe44ee1e800adde754c538e Mon Sep 17 00:00:00 2001 From: rajesh Date: Thu, 2 Apr 2026 16:11:23 +0530 Subject: [PATCH] Implemented spreadsheet logic --- .../constants/GepafinConstant.java | 3 + .../dao/ApplicationEvaluationDao.java | 129 ++++++++++++++++++ src/main/resources/application.properties | 3 + 3 files changed, 135 insertions(+) diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index 43e5d1ea..32f13ea7 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -141,6 +141,9 @@ public class GepafinConstant { public static final String UPDATING_FORM_VALUE_IMPACT_ON_FLOW = "updating.form.value.impact.on.flow"; public static final String APPLICATION_IS_INCOMPLETE_MSG = "application.is.incomplete"; public static final String AUTHORIZATION = "Authorization"; + public static final String BEARER_PREFIX = "Bearer "; + public static final String SPREADSHEET = "spreadsheet"; + public static final String TEMPLATE = "template"; public static final String CHECK_VATNUMBER_URL_V1 = "https://imprese.openapi.it/advance"; public static final String CHECK_VATNUMBER_URL_V2 = "https://company.openapi.com/IT-advanced"; public static final String VAT_CHECK_API_VERSION = "VAT_CHECK_API_VERSION"; diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 6b8d5d50..c0ad6ca2 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -20,8 +20,14 @@ import org.apache.commons.lang3.StringUtils; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import java.math.BigDecimal; @@ -157,6 +163,12 @@ public class ApplicationEvaluationDao { @Value("${default.hub.uuid}") private String defaultHubUuid; + @Value("${excel.fill.api.url}") + private String excelFillApiUrl; + + @Value("${excel.fill.api.token:}") + private String excelFillApiToken; + @Autowired private ApplicationSignedDocumentRepository applicationSignedDocumentRepository; @@ -762,6 +774,11 @@ public class ApplicationEvaluationDao { // Map placeHolders = notificationDao.sendNotificationToBeneficiary(application, NotificationTypeEnum.EVALUATION_CREATION); + // V2 only, idempotent: runs on every create/update of application evaluation (e.g. evaluation created at + // instructor assignment). External fill API is called only for spreadsheet fields that still have no stored + // value. If APPLICATION_EVALUATION_FORM / spreadsheet rows were created here earlier, later + // createApplicationEvaluation and plain updates skip re-fill and leave existing flow unchanged. + populateSpreadsheetEvaluationFormFieldsForV2(application, entity); Map placeHolders = new HashMap<>(); placeHolders.put("{{call_name}}", application.getCall().getName()); String protocolNumber=application.getProtocol().getExternalProtocolNumber(); @@ -2190,6 +2207,118 @@ public class ApplicationEvaluationDao { return response; } + /** + * Evaluation V2 only ({@link ApplicationEntity#getEvaluationVersion()}). + *

+ * Ensures {@link ApplicationEvaluationFormEntity} exists for the call's evaluation form, then for each + * {@code spreadsheet} field calls the external fill API and saves {@code APPLICATION_EVALUATION_FORM_FIELD} + * only when that field has no stored value yet. Safe when this runs at assignment-time (first evaluation create) + * and on later {@code createOrUpdateApplicationEvaluation} updates: no second API call and no overwrite once a + * value is present. {@link #createApplicationEvaluation} continues to work: it reuses the same form row via + * {@link #getApplicationEvaluationFormOrCreate}. + */ + private void populateSpreadsheetEvaluationFormFieldsForV2(ApplicationEntity application, ApplicationEvaluationEntity entity) { + if (!EvaluationVersionEnum.V2.getValue().equals(application.getEvaluationVersion())) { + return; + } + EvaluationFormEntity evaluationFormEntity = evaluationFormRepository.findByCallIdAndIsDeletedFalse(application.getCall().getId()); + if (evaluationFormEntity == null) { + return; + } + ApplicationEvaluationFormEntity applicationEvaluationFormEntity = getApplicationEvaluationFormOrCreate(evaluationFormEntity, entity); + List contentResponseBeans = evaluationFormDao + .convertEvaluationFormEntityToEvaluationFormResponseBean(evaluationFormEntity) + .getContent(); + if (CollectionUtils.isEmpty(contentResponseBeans)) { + return; + } + List spreadsheetUpdates = new ArrayList<>(); + for (ContentResponseBean contentResponseBean : contentResponseBeans) { + if (!GepafinConstant.SPREADSHEET.equals(contentResponseBean.getName())) { + continue; + } + String fieldId = contentResponseBean.getId(); + Optional existingSpreadsheetField = applicationEvaluationFormFieldRepository + .findByApplicationEvaluationFormIdAndFieldIdAndIsDeletedFalse(applicationEvaluationFormEntity.getId(), fieldId); + if (existingSpreadsheetField.isPresent() + && StringUtils.isNotBlank(existingSpreadsheetField.get().getFieldValue())) { + continue; + } + Object populatedSpreadsheetValue = fetchSpreadsheetValueFromExternalApi( + application.getCall().getId(), + application.getId(), + contentResponseBean + ); + if (populatedSpreadsheetValue == null) { + log.warn("Skipping spreadsheet field {}: external API did not return a template workbook value", fieldId); + continue; + } + ApplicationFormFieldRequestBean spreadsheetFieldRequest = new ApplicationFormFieldRequestBean(); + spreadsheetFieldRequest.setFieldId(fieldId); + spreadsheetFieldRequest.setFieldValue(populatedSpreadsheetValue); + spreadsheetUpdates.add(spreadsheetFieldRequest); + } + if (!spreadsheetUpdates.isEmpty()) { + createOrUpdateMultipleFormFields(spreadsheetUpdates, applicationEvaluationFormEntity, evaluationFormEntity); + } + } + + private Object fetchSpreadsheetValueFromExternalApi(Long callId, Long applicationId, ContentResponseBean spreadsheetField) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + if (StringUtils.isNotBlank(excelFillApiToken)) { + headers.set(GepafinConstant.AUTHORIZATION, GepafinConstant.BEARER_PREFIX + excelFillApiToken); + } + + Map requestBody = new LinkedHashMap<>(); + requestBody.put("call_id", callId); + requestBody.put("application_id", applicationId); + requestBody.put(GepafinConstant.TEMPLATE, spreadsheetField); + + try { + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity response = restTemplate.postForEntity( + excelFillApiUrl, + new HttpEntity<>(requestBody, headers), + Object.class + ); + return resolveSpreadsheetFieldValueFromApiResponse(response.getBody()); + } catch (RestClientException exception) { + log.error("Failed to populate spreadsheet field {} from external API", spreadsheetField.getId(), exception); + throw new CustomValidationException(Status.BAD_REQUEST, + "Failed to populate spreadsheet field from external API for fieldId: " + spreadsheetField.getId()); + } + } + + /** + * External fill API returns a spreadsheet field-shaped JSON (id, name, settings, …). Persist only the + * workbook object: {@code settings[name=template].value}. If the response is already a bare workbook + * ({@code sheets} / {@code sheetOrder}), it is stored as-is. + */ + private Object resolveSpreadsheetFieldValueFromApiResponse(Object apiResponseBody) { + if (apiResponseBody == null) { + return null; + } + if (!(apiResponseBody instanceof Map map)) { + return apiResponseBody; + } + Object settingsObj = map.get("settings"); + if (settingsObj instanceof List settingsList) { + for (Object setting : settingsList) { + if (!(setting instanceof Map settingMap)) { + continue; + } + Object name = settingMap.get("name"); + if (GepafinConstant.TEMPLATE.equals(name != null ? name.toString() : null)) { + return settingMap.get("value"); + } + } + log.warn("External spreadsheet API returned settings[] without a template entry"); + return null; + } + return apiResponseBody; + } + private ApplicationEvaluationFormEntity getApplicationEvaluationFormOrCreate(EvaluationFormEntity evaluationFormEntity, ApplicationEvaluationEntity applicationEvaluationEntity) { ApplicationEvaluationFormEntity applicationEvaluationFormEntity = applicationEvaluationFormRepository.findByEvaluationIdAndEvaluationFormId(applicationEvaluationEntity.getId(), evaluationFormEntity.getId()); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7fffc70b..95d1d9b7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -77,6 +77,9 @@ spring.rabbitmq.connection-timeout=120000 app.bandi.login.url.suffix=/loginadmin app.confidi.login.url.suffix=/confidi +excel.fill.api.url=https://excel-gepafin-dev-be.bflows.ai/excel/fill +excel.fill.api.token=a3f8c2d1e4b5a6f7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1 + #sviluppumbria protocol codAoo=SVILUMBRIA-01 sviluppumbria.username=protocollatoresvilumbria