Implemented bulk import of persons
All checks were successful
Build, Push and Deploy / build-and-deploy (push) Successful in 1m43s
All checks were successful
Build, Push and Deploy / build-and-deploy (push) Successful in 1m43s
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package de.iwomm.propify_api.controller;
|
||||
|
||||
import de.iwomm.propify_api.service.OrganizationImportService;
|
||||
import de.iwomm.propify_api.service.PersonImportService;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -13,9 +14,12 @@ import java.io.IOException;
|
||||
public class DataImportController {
|
||||
|
||||
private final PersonImportService personImportService;
|
||||
private final OrganizationImportService organizationImportService;
|
||||
|
||||
public DataImportController(PersonImportService personImportService) {
|
||||
public DataImportController(PersonImportService personImportService,
|
||||
OrganizationImportService organizationImportService) {
|
||||
this.personImportService = personImportService;
|
||||
this.organizationImportService = organizationImportService;
|
||||
}
|
||||
|
||||
@PostMapping("/persons")
|
||||
@@ -39,4 +43,26 @@ public class DataImportController {
|
||||
.body("Unexpected error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/organizations")
|
||||
public ResponseEntity<?> importOrganizations(@RequestParam("file") MultipartFile file) {
|
||||
if (file.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("Please upload a file");
|
||||
}
|
||||
|
||||
if (!file.getOriginalFilename().endsWith(".xlsx")) {
|
||||
return ResponseEntity.badRequest().body("Only .xlsx files are supported");
|
||||
}
|
||||
|
||||
try {
|
||||
OrganizationImportService.ImportResult result = organizationImportService.importOrganizationsFromExcel(file);
|
||||
return ResponseEntity.ok(result);
|
||||
} catch (IOException e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Error processing file: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Unexpected error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package de.iwomm.propify_api.dto;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
public class OrganizationImportDTO {
|
||||
|
||||
@ExcelProperty(index = 0, value = "Organisation - Name")
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(index = 1, value = "Organisation - Branche Intern")
|
||||
private String industry;
|
||||
|
||||
@ExcelProperty(index = 2, value = "Organisation - Label")
|
||||
private String labels;
|
||||
|
||||
@ExcelProperty(index = 3, value = "Organisation - Abgeschlossene Deals")
|
||||
private String completedDeals;
|
||||
|
||||
@ExcelProperty(index = 4, value = "Organisation - Offene Deals")
|
||||
private String openDeals;
|
||||
|
||||
@ExcelProperty(index = 5, value = "Organisation - Datum nächste Aktivität")
|
||||
private String nextActivityDate;
|
||||
|
||||
@ExcelProperty(index = 6, value = "Organisation - Besitzer")
|
||||
private String owner;
|
||||
|
||||
// Getters and Setters
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIndustry() {
|
||||
return industry;
|
||||
}
|
||||
|
||||
public void setIndustry(String industry) {
|
||||
this.industry = industry;
|
||||
}
|
||||
|
||||
public String getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public void setLabels(String labels) {
|
||||
this.labels = labels;
|
||||
}
|
||||
|
||||
public String getCompletedDeals() {
|
||||
return completedDeals;
|
||||
}
|
||||
|
||||
public void setCompletedDeals(String completedDeals) {
|
||||
this.completedDeals = completedDeals;
|
||||
}
|
||||
|
||||
public String getOpenDeals() {
|
||||
return openDeals;
|
||||
}
|
||||
|
||||
public void setOpenDeals(String openDeals) {
|
||||
this.openDeals = openDeals;
|
||||
}
|
||||
|
||||
public String getNextActivityDate() {
|
||||
return nextActivityDate;
|
||||
}
|
||||
|
||||
public void setNextActivityDate(String nextActivityDate) {
|
||||
this.nextActivityDate = nextActivityDate;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,9 @@ public class Organization {
|
||||
@JoinColumn(name = "industry_id")
|
||||
private Industry industry;
|
||||
|
||||
@Column
|
||||
private String owner;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@@ -50,6 +53,14 @@ public class Organization {
|
||||
this.industry = industry;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,199 @@
|
||||
package de.iwomm.propify_api.entity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class Person {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
@Column(nullable = false)
|
||||
private UUID id;
|
||||
|
||||
@Column
|
||||
private String firstName;
|
||||
|
||||
@Column
|
||||
private String lastName;
|
||||
|
||||
@Column
|
||||
private String emailOffice;
|
||||
|
||||
@Column
|
||||
private String emailPrivate;
|
||||
|
||||
@Column
|
||||
private String emailOther;
|
||||
|
||||
@Column
|
||||
private String phoneOfficeCountryCode;
|
||||
|
||||
@Column
|
||||
private String phoneOfficeAreaCode;
|
||||
|
||||
@Column
|
||||
private String phoneOfficeNumber;
|
||||
|
||||
@Column
|
||||
private String phonePrivateCountryCode;
|
||||
|
||||
@Column
|
||||
private String phonePrivateAreaCode;
|
||||
|
||||
@Column
|
||||
private String phonePrivateNumber;
|
||||
|
||||
@Column
|
||||
private String phoneMobile;
|
||||
|
||||
@Column(unique = true)
|
||||
private String keycloakId;
|
||||
|
||||
@Column(nullable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
this.createdAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
|
||||
@JoinTable(
|
||||
name = "person_labels",
|
||||
joinColumns = @JoinColumn(name = "person_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "label_id"),
|
||||
foreignKey = @ForeignKey(name = "fk_person_labels_person", foreignKeyDefinition = "FOREIGN KEY (person_id) REFERENCES person(id) ON DELETE CASCADE")
|
||||
)
|
||||
private Set<PersonLabel> labels = new HashSet<>();
|
||||
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(UUID id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
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 getEmailOffice() {
|
||||
return emailOffice;
|
||||
}
|
||||
|
||||
public void setEmailOffice(String emailOffice) {
|
||||
this.emailOffice = emailOffice;
|
||||
}
|
||||
|
||||
public String getEmailPrivate() {
|
||||
return emailPrivate;
|
||||
}
|
||||
|
||||
public void setEmailPrivate(String emailPrivate) {
|
||||
this.emailPrivate = emailPrivate;
|
||||
}
|
||||
|
||||
public String getEmailOther() {
|
||||
return emailOther;
|
||||
}
|
||||
|
||||
public void setEmailOther(String emailOther) {
|
||||
this.emailOther = emailOther;
|
||||
}
|
||||
|
||||
public String getPhoneOfficeCountryCode() {
|
||||
return phoneOfficeCountryCode;
|
||||
}
|
||||
|
||||
public void setPhoneOfficeCountryCode(String phoneOfficeCountryCode) {
|
||||
this.phoneOfficeCountryCode = phoneOfficeCountryCode;
|
||||
}
|
||||
|
||||
public String getPhoneOfficeAreaCode() {
|
||||
return phoneOfficeAreaCode;
|
||||
}
|
||||
|
||||
public void setPhoneOfficeAreaCode(String phoneOfficeAreaCode) {
|
||||
this.phoneOfficeAreaCode = phoneOfficeAreaCode;
|
||||
}
|
||||
|
||||
public String getPhoneOfficeNumber() {
|
||||
return phoneOfficeNumber;
|
||||
}
|
||||
|
||||
public void setPhoneOfficeNumber(String phoneOfficeNumber) {
|
||||
this.phoneOfficeNumber = phoneOfficeNumber;
|
||||
}
|
||||
|
||||
public String getPhonePrivateCountryCode() {
|
||||
return phonePrivateCountryCode;
|
||||
}
|
||||
|
||||
public void setPhonePrivateCountryCode(String phonePrivateCountryCode) {
|
||||
this.phonePrivateCountryCode = phonePrivateCountryCode;
|
||||
}
|
||||
|
||||
public String getPhonePrivateAreaCode() {
|
||||
return phonePrivateAreaCode;
|
||||
}
|
||||
|
||||
public void setPhonePrivateAreaCode(String phonePrivateAreaCode) {
|
||||
this.phonePrivateAreaCode = phonePrivateAreaCode;
|
||||
}
|
||||
|
||||
public String getPhonePrivateNumber() {
|
||||
return phonePrivateNumber;
|
||||
}
|
||||
|
||||
public void setPhonePrivateNumber(String phonePrivateNumber) {
|
||||
this.phonePrivateNumber = phonePrivateNumber;
|
||||
}
|
||||
|
||||
public String getPhoneMobile() {
|
||||
return phoneMobile;
|
||||
}
|
||||
|
||||
public void setPhoneMobile(String phoneMobile) {
|
||||
this.phoneMobile = phoneMobile;
|
||||
}
|
||||
|
||||
public String getKeycloakId() {
|
||||
return keycloakId;
|
||||
}
|
||||
|
||||
public void setKeycloakId(String keycloakId) {
|
||||
this.keycloakId = keycloakId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public Set<PersonLabel> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public void setLabels(Set<PersonLabel> labels) {
|
||||
this.labels = labels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
package de.iwomm.propify_api.service;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.read.listener.ReadListener;
|
||||
import de.iwomm.propify_api.dto.OrganizationImportDTO;
|
||||
import de.iwomm.propify_api.entity.Industry;
|
||||
import de.iwomm.propify_api.entity.Organization;
|
||||
import de.iwomm.propify_api.repository.IndustryRepository;
|
||||
import de.iwomm.propify_api.repository.OrganizationRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class OrganizationImportService {
|
||||
|
||||
private final OrganizationRepository organizationRepository;
|
||||
private final IndustryRepository industryRepository;
|
||||
|
||||
public OrganizationImportService(OrganizationRepository organizationRepository,
|
||||
IndustryRepository industryRepository) {
|
||||
this.organizationRepository = organizationRepository;
|
||||
this.industryRepository = industryRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ImportResult importOrganizationsFromExcel(MultipartFile file) throws IOException {
|
||||
ImportResult result = new ImportResult();
|
||||
|
||||
EasyExcel.read(file.getInputStream(), OrganizationImportDTO.class, new ReadListener<OrganizationImportDTO>() {
|
||||
private int currentRow = 1; // Start at 1 to account for header
|
||||
|
||||
@Override
|
||||
public void invoke(OrganizationImportDTO data, AnalysisContext context) {
|
||||
currentRow++;
|
||||
try {
|
||||
Organization organization = convertToOrganization(data);
|
||||
organizationRepository.save(organization);
|
||||
result.incrementSuccess();
|
||||
} catch (Exception e) {
|
||||
result.addError(currentRow, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
// Called after all data has been analyzed
|
||||
}
|
||||
}).sheet().doRead();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Organization convertToOrganization(OrganizationImportDTO dto) {
|
||||
Organization organization = new Organization();
|
||||
|
||||
// Name
|
||||
if (dto.getName() != null && !dto.getName().trim().isEmpty()) {
|
||||
organization.setName(dto.getName().trim());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Name is required");
|
||||
}
|
||||
|
||||
// Industry (Branche) - create or lookup
|
||||
if (dto.getIndustry() != null && !dto.getIndustry().trim().isEmpty()) {
|
||||
String industryName = dto.getIndustry().trim();
|
||||
Industry industry = industryRepository.findByName(industryName)
|
||||
.orElseGet(() -> {
|
||||
Industry newIndustry = new Industry();
|
||||
newIndustry.setName(industryName);
|
||||
return industryRepository.save(newIndustry);
|
||||
});
|
||||
organization.setIndustry(industry);
|
||||
}
|
||||
|
||||
// Owner
|
||||
organization.setOwner(trimOrNull(dto.getOwner()));
|
||||
|
||||
return organization;
|
||||
}
|
||||
|
||||
private String trimOrNull(String value) {
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
public static class ImportResult {
|
||||
private int successCount = 0;
|
||||
private int errorCount = 0;
|
||||
private List<ImportError> errors = new ArrayList<>();
|
||||
|
||||
public void incrementSuccess() {
|
||||
successCount++;
|
||||
}
|
||||
|
||||
public void addError(int row, String message) {
|
||||
errorCount++;
|
||||
errors.add(new ImportError(row, message));
|
||||
}
|
||||
|
||||
public int getSuccessCount() {
|
||||
return successCount;
|
||||
}
|
||||
|
||||
public int getErrorCount() {
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
public List<ImportError> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImportError {
|
||||
private int row;
|
||||
private String message;
|
||||
|
||||
public ImportError(int row, String message) {
|
||||
this.row = row;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public int getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user