From d68644c366c79f9532654764f05fe135a70ca485 Mon Sep 17 00:00:00 2001 From: piyushkag Date: Thu, 17 Apr 2025 12:27:38 +0530 Subject: [PATCH] Resolved conflict --- pom.xml | 13 +- .../constants/GepafinConstant.java | 30 ++ .../tendermanagement/dao/ApplicationDao.java | 341 +++++++++++++++++- .../dao/ApplicationEvaluationDao.java | 2 +- .../entities/ApplicationFormView.java | 118 ++++++ .../entities/ApplicationFormViewId.java | 20 + .../ApplicationFormViewRepository.java | 17 + .../service/ApplicationService.java | 4 + .../service/impl/ApplicationServiceImpl.java | 16 + .../gepafin/tendermanagement/util/Utils.java | 26 +- .../web/rest/api/ApplicationApi.java | 23 +- .../api/impl/ApplicationApiController.java | 14 +- .../db/changelog/db.changelog-1.0.0.xml | 5 + .../db/dump/create_application_form_view.sql | 125 +++++++ 14 files changed, 731 insertions(+), 23 deletions(-) create mode 100644 src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java create mode 100644 src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormViewId.java create mode 100644 src/main/java/net/gepafin/tendermanagement/repositories/ApplicationFormViewRepository.java create mode 100644 src/main/resources/db/dump/create_application_form_view.sql diff --git a/pom.xml b/pom.xml index a42534bf..9c84bcc4 100644 --- a/pom.xml +++ b/pom.xml @@ -251,6 +251,13 @@ 0.4.8 + + org.apache.commons + commons-csv + 1.10.0 + + + @@ -271,8 +278,8 @@ liquibase-maven-plugin src/main/resources/application.properties - - - + + + diff --git a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java index b192fba5..4f15580c 100644 --- a/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java +++ b/src/main/java/net/gepafin/tendermanagement/constants/GepafinConstant.java @@ -499,6 +499,36 @@ public class GepafinConstant { public static final String PASSWORD_EXPIRED = "PasswordExpired"; public static final String PASSWORD_EXPIRED_LOGIN_TO_ODESSA = "password.expired.for.login.to.odessa"; + + public static final String APPLICATION="application"; + public static final String APPLICATION_ID="applicationId"; + public static final String USER_WITH_COMPANY_ID="userWithCompanyId"; + public static final String ASSIGNED_USER_ID="assignedUserId"; + public static final String APPLICATION_USER_ID="applicationUserId"; + public static final String INVALID_USER_ID="invalid.user"; + public static final String COMPANY_NAME="companyName"; + public static final String TABLE_COLUMNS="table_columns"; + public static final String PREDEFINED="predefined"; + public static final String EBABLE_CSV="enableCsv"; + public static final String LABEL_CSV="labelCsv"; + public static final String GET="get"; + public static final String REPORT_COLUMNS="reportColumns"; + public static final String FIELD_TYPE="fieldtype"; + public static final String ENABLE_FORMULA="enableFormula"; + public static final String STATE_FIELD_DATA="stateFieldData"; + public static final String REPORT_ENABLE="reportEnable"; + public static final String TABLE="table"; + public static final String GET_FIELD_TYPE="getFieldType"; + public static final String GET_APPLICATION_FORM_ID="getApplicationFormId"; + public static final String GET_APPLICATION_ID="getApplicationId"; + public static final String GET_ID="getId"; + public static final String GET_CLASS="getClass"; + public static final String GET_FORM_ID="getFormId"; + public static final String GET_FIELD_ID="getFieldId"; + public static final String GET_FIELD_LABEL="getFieldLabel"; + public static final String GET_FIELD_VALUE="getFieldValue"; + public static final String GET_REPORT_ENABLE="getReportEnable"; + public static final String GET_REPORT_HEADER="getReportHeader"; } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java index c7592e3e..d6b66527 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationDao.java @@ -1,10 +1,15 @@ package net.gepafin.tendermanagement.dao; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.itextpdf.text.BaseColor; +import com.itextpdf.text.Font; +import com.itextpdf.text.FontFactory; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.Root; import jakarta.persistence.criteria.*; +import jakarta.servlet.http.HttpServletResponse; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.entities.*; @@ -23,15 +28,14 @@ import net.gepafin.tendermanagement.service.FormService; import net.gepafin.tendermanagement.service.HubService; import net.gepafin.tendermanagement.service.SystemEmailTemplatesService; import net.gepafin.tendermanagement.service.UserService; -import net.gepafin.tendermanagement.util.DateTimeUtil; -import net.gepafin.tendermanagement.util.FieldValidator; -import net.gepafin.tendermanagement.util.LoggingUtil; -import net.gepafin.tendermanagement.util.Utils; -import net.gepafin.tendermanagement.util.Validator; +import net.gepafin.tendermanagement.util.*; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; import net.gepafin.tendermanagement.web.rest.api.errors.Status; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.lang3.StringUtils; import org.h2.util.IOUtils; import org.json.JSONObject; import org.slf4j.Logger; @@ -49,17 +53,17 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.lang.reflect.Method; import java.math.BigDecimal; + import java.sql.Timestamp; import java.text.MessageFormat; -import java.text.NumberFormat; -import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -69,7 +73,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.hibernate.validator.internal.engine.messageinterpolation.el.RootResolver.FORMATTER; @Component public class ApplicationDao { @@ -195,6 +198,15 @@ public class ApplicationDao { @Autowired private ApplicationEvaluationRepository applicationEvaluationRepository; + @Autowired + private ApplicationFormViewRepository applicationFormViewRepository; + + @Autowired + private FormRepository formRepository; + + @Autowired + private ApplicationEvaluationDao applicationEvaluationDao; + public ApplicationResponseBean createApplication(HttpServletRequest request, ApplicationRequestBean applicationRequestBean, Long formId, Long applicationId) { FormEntity formEntity = formService.validateForm(formId); @@ -1878,4 +1890,313 @@ public class ApplicationDao { } return application; } + public List getApplicationFormData(Long callId) { + List applicationFormViews=new ArrayList<>(); + + applicationFormViews= applicationFormViewRepository.findByCallId(callId); + + return applicationFormViews; + } + + private List getStaticGetterMethods() { + List excluded = Arrays.asList( + GepafinConstant.GET_FIELD_TYPE,GepafinConstant.GET_APPLICATION_FORM_ID,GepafinConstant.GET_ID,GepafinConstant.GET_CLASS,GepafinConstant.GET_FORM_ID,GepafinConstant.GET_FIELD_ID,GepafinConstant.GET_FIELD_LABEL,GepafinConstant.GET_FIELD_VALUE,GepafinConstant.GET_REPORT_HEADER,GepafinConstant.GET_REPORT_ENABLE + ); + + Method applicationIdMethod = null; + + List methods = new ArrayList<>(); + + for (Method m : ApplicationFormView.class.getMethods()) { + if (m.getName().equals(GepafinConstant.GET_APPLICATION_ID)) { + applicationIdMethod = m; + } else if (m.getName().startsWith(GepafinConstant.GET) && m.getParameterCount() == 0 && !excluded.contains(m.getName())) { + methods.add(m); + } + } + + methods.sort(Comparator.comparing(Method::getName)); // Sort remaining + + if (applicationIdMethod != null) { + methods.add(0, applicationIdMethod); // Add it to the beginning + } + + return methods; + + } + + private String methodToHeader(Method method) { + String name = method.getName().substring(3); // strip "get" + return Arrays.stream(name.split("(?=[A-Z])")) + .map(String::toLowerCase) + .map(word -> Character.toUpperCase(word.charAt(0)) + word.substring(1)) + .collect(Collectors.joining(" ")); + } + + private String invokeGetter(ApplicationFormView view, Method method) { + try { + Object value = method.invoke(view); + if (value == null) return ""; + + String stringValue; + + if (value instanceof LocalDate) { + stringValue = ((LocalDate) value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } else if (value instanceof LocalDateTime) { + stringValue = ((LocalDateTime) value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } else if (value instanceof Date) { + stringValue = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) value); + } else { + stringValue = value.toString(); + } + + // Wrap it in ="..." to make Excel treat it as a literal string + return "=\"" + stringValue.replace("\"", "\"\"") + "\""; + + } catch (Exception e) { + return ""; + } + } + + + public byte[] exportCsv(Long callId) { + + List results = getApplicationFormData(callId); + + + Map appInfo = new HashMap<>(); + Map> appFieldValues = new LinkedHashMap<>(); + Set tableFieldIds = new HashSet<>(); + Map fieldIdToLabel = new LinkedHashMap<>(); + + for (ApplicationFormView row : results) { + appInfo.putIfAbsent(row.getApplicationId(), row); + String label=row.getReportHeader(); + if(Boolean.TRUE.equals(StringUtils.isEmpty(label))){ + label=row.getFieldLabel(); + } + fieldIdToLabel.putIfAbsent(row.getFieldId(), label); + + if (GepafinConstant.TABLE.equalsIgnoreCase(row.getFieldType())) { + tableFieldIds.add(row.getFieldId()); + continue; + } + + String value = Optional.ofNullable(row.getFieldValue()) + .map(v -> v.startsWith("\"") && v.endsWith("\"") ? v.substring(1, v.length() - 1) : v) + .orElse(""); + + appFieldValues + .computeIfAbsent(row.getApplicationId(), k -> new LinkedHashMap<>()) + .merge(row.getFieldId(), value, (v1, v2) -> v1.equals(v2) ? v1 : String.join(", ", v1, v2)); + } + + Map> tableHeadersByFieldId = new LinkedHashMap<>(); + Map> tableDataByApp = new HashMap<>(); + prepareTableFieldData(results, tableFieldIds, tableHeadersByFieldId, tableDataByApp); + + // Final header construction + List staticMethods = getStaticGetterMethods(); + List staticHeaders = staticMethods.stream().map(this::methodToHeader).toList(); + + List dynamicHeaders = new ArrayList<>(); + for (String fieldId : fieldIdToLabel.keySet()) { + if (tableHeadersByFieldId.containsKey(fieldId)) { + dynamicHeaders.addAll(tableHeadersByFieldId.get(fieldId)); + } else { + dynamicHeaders.add(fieldIdToLabel.get(fieldId)); + } + } + + List allHeaders = new ArrayList<>(staticHeaders); + allHeaders.addAll(dynamicHeaders); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try (CSVPrinter printer = new CSVPrinter(new OutputStreamWriter(out), CSVFormat.DEFAULT.withHeader(allHeaders.toArray(new String[0])))) { + for (Long appId : appFieldValues.keySet()) { + ApplicationFormView appRow = appInfo.get(appId); + Map flatFieldVals = appFieldValues.get(appId); + Map tableVals = tableDataByApp.getOrDefault(appId, Collections.emptyMap()); + + List row = new ArrayList<>(); + for (Method method : staticMethods) { + row.add(invokeGetter(appRow, method)); + } + + for (String fieldId : fieldIdToLabel.keySet()) { + if (tableHeadersByFieldId.containsKey(fieldId)) { + for (String header : tableHeadersByFieldId.get(fieldId)) { + row.add(tableVals.getOrDefault(header, "")); + } + } else { + row.add(flatFieldVals.getOrDefault(fieldId, "")); + } + } + + printer.printRecord(row); + } + + } catch (IOException e) { + throw new RuntimeException("CSV generation failed", e); + } + + return out.toByteArray(); + } + private Map extractTableData(String fieldType, + ContentResponseBean content, String fieldValue) { + + if (content == null) return Map.of(); + + Map result = new LinkedHashMap<>(); + + List> rows = null; + try { + rows = GepafinConstant.CRITERIA_TABLE_COLUMNS.equals(fieldType) + ? Utils.convertJsonToListMap(String.valueOf(PdfUtils.extractRows(fieldValue))) + : Utils.convertJsonToListMap(fieldValue); + } catch (Exception e) { + throw new RuntimeException(e); + } + + Map fieldLabelMap = new LinkedHashMap<>(); + Set predefinedIds = new LinkedHashSet<>(); + Set dynamicIds = new LinkedHashSet<>(); + Set numericFormulaIds = new LinkedHashSet<>(); + + for (SettingResponseBean setting : content.getSettings()) { + String settingName = setting.getName(); + if(settingName.equals(GepafinConstant.REPORT_ENABLE)){ + Boolean enable= (Boolean) setting.getValue(); + if(Boolean.FALSE.equals(enable)){ + return null; + } + } + + if (Boolean.TRUE.equals(GepafinConstant.TABLE_COLUMNS.equals(settingName)) || Boolean.TRUE.equals(GepafinConstant.CRITERIA_TABLE_COLUMNS.equals(settingName))) { + Map valueMap = (Map) setting.getValue(); + if (valueMap == null) continue; + + List> columns = (List>) valueMap.get(GepafinConstant.STATE_FIELD_DATA); + if (columns != null) { + for (Map col : columns) { + String id = String.valueOf(col.get(GepafinConstant.NAME)); + if (Boolean.FALSE.equals(col.get(GepafinConstant.PREDEFINED))) { + if (GepafinConstant.NUMERIC.equals(col.get(GepafinConstant.FIELD_TYPE)) && Boolean.TRUE.equals(col.get(GepafinConstant.ENABLE_FORMULA))) { + numericFormulaIds.add(id); + } + } + } + } + } + + if (Boolean.TRUE.equals(GepafinConstant.REPORT_COLUMNS.equals(settingName))) { + List> reportColumns = (List>) setting.getValue(); + if (reportColumns != null) { + for (Map col : reportColumns) { + Boolean enableCsv = (Boolean) col.get(GepafinConstant.EBABLE_CSV); + if (Boolean.TRUE.equals(enableCsv)) { + String id = String.valueOf(col.get(GepafinConstant.NAME)); + + String fieldCsvLabel = col.get(GepafinConstant.LABEL_CSV) != null + ? String.valueOf(col.get(GepafinConstant.LABEL_CSV)) + : String.valueOf(col.get(GepafinConstant.LABEL)); + + fieldLabelMap.put(id, fieldCsvLabel); + + if (Boolean.TRUE.equals(col.get(GepafinConstant.PREDEFINED))) { + predefinedIds.add(id); + } else { + dynamicIds.add(id); + } + } + } + } + } + } + + + + if (predefinedIds.isEmpty()) { + return null; + } + for (Map row : rows) { + String prefix = predefinedIds.stream() + .map(id -> String.valueOf(row.getOrDefault(id, ""))) + .filter(s -> !s.isBlank()) + .findFirst().orElse(""); + + + for (String dynId : dynamicIds) { + String dynLabel = fieldLabelMap.get(dynId); + for (String preId : predefinedIds.isEmpty() ? List.of("") : predefinedIds) { + String preLabel = fieldLabelMap.get(preId); + String key = dynLabel + " " + (preLabel != null ? preLabel + " " : "") + prefix; + result.put(key, String.valueOf(row.getOrDefault(dynId, ""))); + } + } + } + + // Add totals for numeric formula-enabled columns + for (String dynId : numericFormulaIds) { + double sum = rows.stream() + .mapToDouble(r -> { + try { + return Double.parseDouble(String.valueOf(r.getOrDefault(dynId, "0"))); + } catch (NumberFormatException e) { + return 0.0; + } + }).sum(); + String dynLabel = fieldLabelMap.get(dynId); + result.put("TOTAL " + dynLabel, String.valueOf(sum)); + } + + return result; + } + private void prepareTableFieldData( + List results, + Set tableFieldIds, + Map> tableHeadersByFieldId, + Map> tableDataByApp) { + + if (tableFieldIds.isEmpty()) return; + + Map> groupedByApp = results.stream() + .filter(r -> tableFieldIds.contains(r.getFieldId())) + .collect(Collectors.groupingBy(ApplicationFormView::getApplicationId)); + + for (Map.Entry> entry : groupedByApp.entrySet()) { + Long appId = entry.getKey(); + Map flattenedAll = new LinkedHashMap<>(); + + for (ApplicationFormView row : entry.getValue()) { + formRepository.findById(row.getFormId()).ifPresent(form -> { + List contentList = Utils.convertJsonStringToList(form.getContent(), ContentResponseBean.class); + ContentResponseBean content = contentList.stream() + .filter(c -> c.getId().equals(row.getFieldId())) + .findFirst() + .orElse(null); + + if (content == null) return; + + content.getSettings().stream() + .filter(setting -> GepafinConstant.TABLE_COLUMNS.equals(setting.getName()) + || GepafinConstant.CRITERIA_TABLE_COLUMNS.equals(setting.getName())) + .findFirst() + .ifPresent(setting -> { + Map flattened = extractTableData( + row.getFieldType(), + content, + row.getFieldValue() + ); + if (flattened != null) { + tableHeadersByFieldId.putIfAbsent(row.getFieldId(), new ArrayList<>(flattened.keySet())); + flattenedAll.putAll(flattened); + } + }); + }); + } + + tableDataByApp.put(appId, flattenedAll); + } + } } diff --git a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java index 17cac4c4..7052aad0 100644 --- a/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java +++ b/src/main/java/net/gepafin/tendermanagement/dao/ApplicationEvaluationDao.java @@ -1665,7 +1665,7 @@ public class ApplicationEvaluationDao { }); } - private String getLabelFromSettings(ContentResponseBean contentResponseBean) { + public String getLabelFromSettings(ContentResponseBean contentResponseBean) { String label = contentResponseBean.getLabel(); if (contentResponseBean.getSettings() != null) { for (SettingResponseBean setting : contentResponseBean.getSettings()) { diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java new file mode 100644 index 00000000..b3733afc --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormView.java @@ -0,0 +1,118 @@ +package net.gepafin.tendermanagement.entities; + + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.Immutable; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalTime; + +@Entity +@Immutable +@Table(name = "application_form_view") +@Getter +@Setter +@IdClass(ApplicationFormViewId.class) +public class ApplicationFormView { + + @Id + @Column(name = "id") + private Long id; + + @Column(name = "application_id") + private Long applicationId; + + @Column(name = "call_id") + private Long callId; + + @Column(name = "form_id") + private Long formId; + + @Column(name = "application_form_id") + private Long applicationFormId; + + @Column(name = "field_id") + private String fieldId; + + @Column(name = "field_label") + private String fieldLabel; + + @Column(name = "field_type") + private String fieldType; + + @Column(name = "field_value") + private String fieldValue; + + @Column(name = "report_enable") + private Boolean reportEnable; + + @Column(name = "report_header") + private String reportHeader; + + @Column(name = "status") + private String status; + + @Column(name = "amount_requested") + private BigDecimal amountRequested; + + @Column(name = "amount_accepted") + private BigDecimal amountAccepted; + + @Column(name = "is_deleted") + private boolean isDeleted; + + @Column(name = "hub_id") + private Long hubId; + + @Column(name = "user_id") + private Long userId; + + @Column(name = "evaluation_version") + private String evaluationVersion; + + @Column(name = "company_id") + private Long companyId; + + @Column(name = "company_name") + private String companyName; + + @Column(name = "company_vat_number") + private String companyVatNumber; + + @Column(name = "codice_ateco") + private String codiceAteco; + + @Column(name = "company_codice_fiscale") + private String companyCodiceFiscale; + + @Column(name = "protocol_number") + private Long protocolNumber; + + @Column(name = "user_codice_fiscale") + private String userCodiceFiscale; + + @Column(name = "user_name") + private String userName; + + @Column(name = "legal_representative") + private Boolean legalRepresentative; + + @Column(name = "call_title") + private String callTitle; + + @Column(name = "call_end_date") + private LocalDate callEndDate; + + @Column(name = "call_end_time") + private LocalTime callEndTime; + + @Column(name = "call_start_date") + private LocalDate callStartDate; + + @Column(name = "call_start_time") + private LocalTime callStartTime; + +} diff --git a/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormViewId.java b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormViewId.java new file mode 100644 index 00000000..37d0cbb7 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/entities/ApplicationFormViewId.java @@ -0,0 +1,20 @@ +package net.gepafin.tendermanagement.entities; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Objects; + +@Data +public class ApplicationFormViewId implements Serializable { + + private static final long serialVersionUID = 1L; + private Long id; + + public ApplicationFormViewId() { + } + + public ApplicationFormViewId(Long id) { + this.id = id; + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationFormViewRepository.java b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationFormViewRepository.java new file mode 100644 index 00000000..ecfe17c7 --- /dev/null +++ b/src/main/java/net/gepafin/tendermanagement/repositories/ApplicationFormViewRepository.java @@ -0,0 +1,17 @@ +package net.gepafin.tendermanagement.repositories; + +import net.gepafin.tendermanagement.entities.ApplicationFormView; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ApplicationFormViewRepository extends JpaRepository, JpaSpecificationExecutor { + + @Query("SELECT v FROM ApplicationFormView v WHERE v.callId = :callId AND v.reportEnable = true") + List findByCallId(Long callId); + +} diff --git a/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java b/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java index 4565668c..24fde801 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java +++ b/src/main/java/net/gepafin/tendermanagement/service/ApplicationService.java @@ -1,6 +1,7 @@ package net.gepafin.tendermanagement.service; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import net.gepafin.tendermanagement.entities.ApplicationEntity; import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean; import net.gepafin.tendermanagement.model.request.ApplicationRequest; @@ -46,4 +47,7 @@ public interface ApplicationService { PageableResponseBean> getAllApplicationByPagination(HttpServletRequest request, Long callId, Long companyId, ApplicationPageableRequestBean applicationPageableRequestBean); public ApplicationEntity validateApplicationWithCompany(Long applicationId,Long companyId); + + public byte[] exportCsv(HttpServletRequest request, Long callId); + } diff --git a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java index 8426390e..ea3f22c2 100644 --- a/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java +++ b/src/main/java/net/gepafin/tendermanagement/service/impl/ApplicationServiceImpl.java @@ -6,6 +6,7 @@ import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.dao.ApplicationDao; import net.gepafin.tendermanagement.dao.FlowFormDao; import net.gepafin.tendermanagement.entities.ApplicationEntity; +import net.gepafin.tendermanagement.entities.CallEntity; import net.gepafin.tendermanagement.entities.CompanyEntity; import net.gepafin.tendermanagement.entities.UserEntity; import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean; @@ -15,6 +16,7 @@ import net.gepafin.tendermanagement.enums.FormActionEnum; import net.gepafin.tendermanagement.model.request.ApplicationRequestBean; import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.service.ApplicationService; +import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.util.Validator; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import net.gepafin.tendermanagement.web.rest.api.errors.ForbiddenAccessException; @@ -38,6 +40,9 @@ public class ApplicationServiceImpl implements ApplicationService { @Autowired private Validator validator; + @Autowired + private CallService callService; + @Override @Transactional(rollbackFor = Exception.class) public ApplicationResponseBean createApplication(HttpServletRequest request, @@ -152,4 +157,15 @@ public class ApplicationServiceImpl implements ApplicationService { public ApplicationEntity validateApplicationWithCompany(Long applicationId,Long companyId) { return applicationDao.validateApplicationWithCompany(applicationId,companyId); } + + @Override + public byte[] exportCsv(HttpServletRequest request, Long callId) { + UserEntity userEntity = validator.validateUser(request); + CallEntity call=callService.validateCall(callId); + validator.validateHubId(request,call.getHub().getId()); + byte[] csvBytes= applicationDao.exportCsv(callId); + + return csvBytes; + } + } diff --git a/src/main/java/net/gepafin/tendermanagement/util/Utils.java b/src/main/java/net/gepafin/tendermanagement/util/Utils.java index 1838bd57..30ac3687 100644 --- a/src/main/java/net/gepafin/tendermanagement/util/Utils.java +++ b/src/main/java/net/gepafin/tendermanagement/util/Utils.java @@ -796,4 +796,28 @@ public class Utils { return null; } } -} \ No newline at end of file + public static List> convertJsonToListMap(String jsonString) { + try { + if (jsonString == null || jsonString.trim().isEmpty()) { + return Collections.emptyList(); + } + + ObjectMapper objectMapper = new ObjectMapper(); + String unescaped; + + // First try: parse as if it's double-encoded (escaped string containing a JSON array) + try { + unescaped = objectMapper.readValue(jsonString, String.class); + } catch (Exception e) { + // If that fails, assume it's already a proper JSON array + unescaped = jsonString; + } + + // Now parse the actual JSON array + return objectMapper.readValue(unescaped, new TypeReference>>() {}); + } catch (Exception e) { + e.printStackTrace(); + return Collections.emptyList(); + } + } +} diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java index 9db1d79e..00609f68 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/ApplicationApi.java @@ -2,11 +2,12 @@ package net.gepafin.tendermanagement.web.rest.api; import java.util.List; -import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean; -import net.gepafin.tendermanagement.model.request.NotificationRequestBean; +import jakarta.servlet.http.HttpServletResponse; +import net.gepafin.tendermanagement.model.request.*; import net.gepafin.tendermanagement.model.response.*; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -18,10 +19,8 @@ import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; -import net.gepafin.tendermanagement.model.request.ApplicationRequest; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; import net.gepafin.tendermanagement.enums.FormActionEnum; -import net.gepafin.tendermanagement.model.request.ApplicationRequestBean; import net.gepafin.tendermanagement.model.util.Response; import net.gepafin.tendermanagement.web.rest.api.errors.ErrorConstants; @@ -224,5 +223,21 @@ public interface ApplicationApi { ResponseEntity>>> getAllApplicationByPagination(HttpServletRequest request,@Parameter(description = "The call id", required = false) @RequestParam(value = "callId", required = false) Long callId, @Parameter(description = "The company id", required = false) @RequestParam(value = "companyId", required = false) Long companyId, @RequestBody ApplicationPageableRequestBean applicationPageableRequestBean); + @Operation(summary = "Api to download application data as a CSV file using the call ID", + responses = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "404", description = "Not Found", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.NOTFOUND_ERROR_EXAMPLE)})), + @ApiResponse(responseCode = "401", description = "Unauthorized", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.UNAUTHORIZED_ERROR_EXAMPLE)})), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, examples = { + @ExampleObject(value = ErrorConstants.BADREQUEST_ERROR_EXAMPLE)})) + }) + @GetMapping(value = "/call/{callId}/csv") + @PreAuthorize("hasRole('ROLE_SUPER_ADMIN')") + public ResponseEntity exportCsv( + HttpServletRequest request, @Parameter(description = "The call id", required = true) @PathVariable(value = "callId", required = true) Long callId); + + } diff --git a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java index a9c148bb..a06f5dc4 100644 --- a/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java +++ b/src/main/java/net/gepafin/tendermanagement/web/rest/api/impl/ApplicationApiController.java @@ -1,16 +1,14 @@ package net.gepafin.tendermanagement.web.rest.api.impl; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.enums.UserActionContextEnum; import net.gepafin.tendermanagement.enums.UserActionLogsEnum; -import net.gepafin.tendermanagement.model.request.ApplicationPageableRequestBean; -import net.gepafin.tendermanagement.model.request.ApplicationRequest; +import net.gepafin.tendermanagement.model.request.*; import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum; import net.gepafin.tendermanagement.enums.FormActionEnum; -import net.gepafin.tendermanagement.model.request.ApplicationRequestBean; -import net.gepafin.tendermanagement.model.request.UserActionRequest; import net.gepafin.tendermanagement.model.response.*; import net.gepafin.tendermanagement.model.util.Response; import net.gepafin.tendermanagement.service.ApplicationService; @@ -236,5 +234,13 @@ public class ApplicationApiController implements ApplicationApi { .getAllApplicationByPagination(request, callId,companyId,applicationPageableRequestBean); return ResponseEntity.status(HttpStatus.OK).body(new Response<>(pageableResponseBean, Status.SUCCESS, Translator.toLocale(GepafinConstant.GET_APPLICATION_SUCCESS_MSG))); } + @Override + public ResponseEntity exportCsv(HttpServletRequest request, Long callId) { + byte[] csvBytes =applicationService.exportCsv(request,callId); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=applications.csv") + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(csvBytes); + } } diff --git a/src/main/resources/db/changelog/db.changelog-1.0.0.xml b/src/main/resources/db/changelog/db.changelog-1.0.0.xml index 82104fcb..7e349dea 100644 --- a/src/main/resources/db/changelog/db.changelog-1.0.0.xml +++ b/src/main/resources/db/changelog/db.changelog-1.0.0.xml @@ -2678,4 +2678,9 @@ + + + diff --git a/src/main/resources/db/dump/create_application_form_view.sql b/src/main/resources/db/dump/create_application_form_view.sql new file mode 100644 index 00000000..b84eaa25 --- /dev/null +++ b/src/main/resources/db/dump/create_application_form_view.sql @@ -0,0 +1,125 @@ +CREATE OR REPLACE VIEW gepafin_schema.application_form_view AS +SELECT app_data.id, + app_data.call_id, + app_data.application_form_id, + app_data.form_id, + app_data.application_id, + field_data.value ->> 'id' AS field_id, + COALESCE( + (SELECT s.value ->> 'value' + FROM jsonb_array_elements(field_data.value -> 'settings') s(value) + WHERE s.value ->> 'name' = 'label' + LIMIT 1), + field_data.value ->> 'label' + ) AS field_label, + ( + SELECT (s.value ->> 'value')::boolean + FROM jsonb_array_elements(field_data.value -> 'settings') s(value) + WHERE s.value ->> 'name' = 'reportEnable' + LIMIT 1 + ) AS report_enable, + COALESCE( (SELECT s.value ->> 'value' + FROM jsonb_array_elements(field_data.value -> 'settings') s(value) + WHERE s.value ->> 'name' = 'reportHeader' + LIMIT 1), + field_data.value ->> 'reportHeader' + ) AS report_header, + field_data.value ->> 'name' AS field_type, + CASE + WHEN field_data.value ->> 'name' = 'fileupload' THEN + to_jsonb(( + SELECT string_agg(d.file_name, ', ') + FROM unnest(string_to_array(app_data.field_value, ',')) file_ids(file_id) + JOIN gepafin_schema.document d ON d.id::text = file_ids.file_id + WHERE d.is_deleted = false + )) + WHEN field_data.value ->> 'name' IN ('checkboxes', 'select', 'radio') THEN + CASE + WHEN app_data.field_value ~~ '[%' THEN + to_jsonb(( + SELECT string_agg(opt.value ->> 'label', ', ') + FROM jsonb_array_elements_text(app_data.field_value::jsonb) selected_id(value) + CROSS JOIN LATERAL ( + SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings') s(value) + WHERE s.value ->> 'name' = 'options' + ) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value') opt(value) + WHERE opt.value ->> 'name' = selected_id.value + )) + ELSE + to_jsonb(( + SELECT opt.value ->> 'label' + FROM ( + SELECT s.value + FROM jsonb_array_elements(field_data.value -> 'settings') s(value) + WHERE s.value ->> 'name' = 'options' + ) options_setting, + LATERAL jsonb_array_elements(options_setting.value -> 'value') opt(value) + WHERE opt.value ->> 'name' = app_data.field_value + LIMIT 1 + )) + END + ELSE + to_jsonb(app_data.field_value) + END AS field_value, + app_data.status, + app_data.amount_requested, + app_data.amount_accepted, + app_data.is_deleted, + app_data.hub_id, + app_data.user_id, + app_data.evaluation_version, + app_data.company_id, + c.company_name, + c.vat_number AS company_vat_number, + c.codice_ateco, + c.codice_fiscale AS company_codice_fiscale, + p.protocol_number, + b.codice_fiscale AS user_codice_fiscale, + COALESCE(NULLIF(TRIM(BOTH FROM CONCAT(COALESCE(u.first_name, ''), ' ', COALESCE(u.last_name, ''))), ''), '') AS user_name, + uwc.is_legal_representant AS legal_representative, + cl.name AS call_title, + cl.end_date AS call_end_date, + cl.end_time AS call_end_time, + cl.start_date AS call_start_date, + cl.start_time AS call_start_time +FROM ( + SELECT a.id AS application_id, + a.call_id, + a.protocol_number, + af.id AS application_form_id, + af.form_id AS form_id, + aff.id AS id, + aff.field_value, + a.status, + a.amount_requested, + a.amount_accepted, + a.is_deleted, + a.hub_id, + a.user_id, + a.evaluation_version, + a.created_date, + a.company_id, + aff.field_id, + f.content + FROM gepafin_schema.application a + JOIN gepafin_schema.application_form af ON af.application_id = a.id + JOIN gepafin_schema.application_form_field aff ON aff.application_form_id = af.id + JOIN gepafin_schema.form f ON f.id = af.form_id + WHERE a.is_deleted = false +) app_data +CROSS JOIN LATERAL ( + SELECT jsonb_array_elements.value + FROM jsonb_array_elements(app_data.content::jsonb) jsonb_array_elements(value) + WHERE jsonb_array_elements.value ->> 'id' = app_data.field_id::text +) field_data(value) +LEFT JOIN gepafin_schema.call cl ON app_data.call_id = cl.id +LEFT JOIN gepafin_schema.company c ON app_data.company_id = c.id +LEFT JOIN gepafin_schema.protocol p ON app_data.protocol_number = p.id +LEFT JOIN gepafin_schema.gepafin_user u ON app_data.user_id = u.id +LEFT JOIN gepafin_schema.user_with_company uwc ON app_data.user_id = uwc.user_id AND app_data.company_id = uwc.company_id AND uwc.is_deleted = false +LEFT JOIN gepafin_schema.beneficiary b ON u.beneficiary_id = b.id +WHERE app_data.id IS NOT NULL + AND app_data.status NOT IN ('DRAFT', 'AWAITING', 'READY') +ORDER BY app_data.id, field_data.value ->> 'id'; \ No newline at end of file