From 62c13ff0b17e106cc5e98c760362b2f9cfff7a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Murat=20O=CC=88zkorkmaz?= Date: Tue, 4 Nov 2025 13:40:33 +0100 Subject: [PATCH] Implemented bulk import of persons --- bruno/propify/DataImport/Auth Bruno.bru | 2 +- .../DataImport/Upload Organizations.bru | 39 ++++ .../{Upload files.bru => Upload Persons.bru} | 4 +- init_data/organizations-17911945-23.xlsx | Bin 0 -> 41242 bytes .../controller/DataImportController.java | 28 ++- .../dto/OrganizationImportDTO.java | 84 ++++++++ .../propify_api/entity/Organization.java | 11 + .../de/iwomm/propify_api/entity/Person.java | 197 +++++++++++++++++- .../service/OrganizationImportService.java | 137 ++++++++++++ 9 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 bruno/propify/DataImport/Upload Organizations.bru rename bruno/propify/DataImport/{Upload files.bru => Upload Persons.bru} (95%) create mode 100644 init_data/organizations-17911945-23.xlsx create mode 100644 src/main/java/de/iwomm/propify_api/dto/OrganizationImportDTO.java create mode 100644 src/main/java/de/iwomm/propify_api/service/OrganizationImportService.java diff --git a/bruno/propify/DataImport/Auth Bruno.bru b/bruno/propify/DataImport/Auth Bruno.bru index aec34d0..d24f723 100644 --- a/bruno/propify/DataImport/Auth Bruno.bru +++ b/bruno/propify/DataImport/Auth Bruno.bru @@ -1,7 +1,7 @@ meta { name: Auth Bruno type: http - seq: 2 + seq: 1 } post { diff --git a/bruno/propify/DataImport/Upload Organizations.bru b/bruno/propify/DataImport/Upload Organizations.bru new file mode 100644 index 0000000..2934ebb --- /dev/null +++ b/bruno/propify/DataImport/Upload Organizations.bru @@ -0,0 +1,39 @@ +meta { + name: Upload Organizations + type: http + seq: 3 +} + +post { + url: {{API_BASE_URL}}/api/{{API_VERSION}}/data-import/organizations + body: multipartForm + auth: bearer +} + +auth:bearer { + token: {{BEARER_TOKEN}} +} + +body:json { + { + "name": "Bungalow", + "street": "Hebbelstraße", + "houseNumber": "30", + "zipCode": "55127", + "city": "Mainz", + "country": "DE", + "notes": "Lorem ipsum" + } +} + +body:multipart-form { + file: @file(/Users/muratoezkorkmaz/projects/misc/skamp/init_data/organizations-17911945-23.xlsx) +} + +body:file { + file: @file(/Users/murat/Pictures/IMG_0229.jpeg) @contentType(image/jpeg) +} + +settings { + encodeUrl: true +} diff --git a/bruno/propify/DataImport/Upload files.bru b/bruno/propify/DataImport/Upload Persons.bru similarity index 95% rename from bruno/propify/DataImport/Upload files.bru rename to bruno/propify/DataImport/Upload Persons.bru index 92023f3..2a42436 100644 --- a/bruno/propify/DataImport/Upload files.bru +++ b/bruno/propify/DataImport/Upload Persons.bru @@ -1,7 +1,7 @@ meta { - name: Upload files + name: Upload Persons type: http - seq: 1 + seq: 2 } post { diff --git a/init_data/organizations-17911945-23.xlsx b/init_data/organizations-17911945-23.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..96d24db2fbc516b3d746b1f10e4d8f49b545c6b2 GIT binary patch literal 41242 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 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()); + } + } } diff --git a/src/main/java/de/iwomm/propify_api/dto/OrganizationImportDTO.java b/src/main/java/de/iwomm/propify_api/dto/OrganizationImportDTO.java new file mode 100644 index 0000000..2b9e9e4 --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/dto/OrganizationImportDTO.java @@ -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; + } +} diff --git a/src/main/java/de/iwomm/propify_api/entity/Organization.java b/src/main/java/de/iwomm/propify_api/entity/Organization.java index 4162215..6a1bf7a 100644 --- a/src/main/java/de/iwomm/propify_api/entity/Organization.java +++ b/src/main/java/de/iwomm/propify_api/entity/Organization.java @@ -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; } diff --git a/src/main/java/de/iwomm/propify_api/entity/Person.java b/src/main/java/de/iwomm/propify_api/entity/Person.java index 4f8138a..89182be 100644 --- a/src/main/java/de/iwomm/propify_api/entity/Person.java +++ b/src/main/java/de/iwomm/propify_api/entity/Person.java @@ -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 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 getLabels() { + return labels; + } + + public void setLabels(Set labels) { + this.labels = labels; + } +} diff --git a/src/main/java/de/iwomm/propify_api/service/OrganizationImportService.java b/src/main/java/de/iwomm/propify_api/service/OrganizationImportService.java new file mode 100644 index 0000000..2dd2f8a --- /dev/null +++ b/src/main/java/de/iwomm/propify_api/service/OrganizationImportService.java @@ -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() { + 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 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 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; + } + } +}