From 3a66a28a33d741d7978f09b45c8be9d84ae20de7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 09:53:39 +0100 Subject: [PATCH] import operational contact + allow multiple assignments of contacts --- .../HsOfficeBankAccountEntity.java | 3 +- .../office/debitor/HsOfficeDebitorEntity.java | 3 +- .../membership/HsOfficeMembershipEntity.java | 3 +- .../partner/HsOfficePartnerDetailsEntity.java | 3 +- .../office/person/HsOfficePersonEntity.java | 3 +- .../HsOfficeRelationshipEntity.java | 14 ++- .../office/migration/ImportOfficeTables.java | 105 ++++++++++++------ src/test/resources/migration/contacts.csv | 6 +- 8 files changed, 99 insertions(+), 41 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java index bbaacb1d..fd6b0c44 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.bankaccount; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -23,7 +24,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @AllArgsConstructor @FieldNameConstants @DisplayName("BankAccount") -public class HsOfficeBankAccountEntity implements Stringifyable { +public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable { private static Stringify toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount") .withProp(Fields.holder, HsOfficeBankAccountEntity::getHolder) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java index 61b8bbd8..f6c46772 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java @@ -4,6 +4,7 @@ import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -23,7 +24,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("Debitor") -public class HsOfficeDebitorEntity implements Stringifyable { +public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { // TODO: I would rather like to generate something matching this example: // debitor(1234500: Test AG, tes) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java index b637d3a7..9f8cb688 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntity.java @@ -5,6 +5,7 @@ import com.vladmihalcea.hibernate.type.range.Range; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -27,7 +28,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("Membership") -public class HsOfficeMembershipEntity implements Stringifyable { +public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { private static Stringify stringify = stringify(HsOfficeMembershipEntity.class) .withProp(HsOfficeMembershipEntity::getMemberNumber) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java index abfd5d8b..d7c50779 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.partner; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -19,7 +20,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("PartnerDetails") -public class HsOfficePartnerDetailsEntity implements Stringifyable { +public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable { private static Stringify stringify = stringify( HsOfficePartnerDetailsEntity.class, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java index a76d4130..932c92a4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.person; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.apache.commons.lang3.StringUtils; @@ -21,7 +22,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @AllArgsConstructor @FieldNameConstants @DisplayName("Person") -public class HsOfficePersonEntity implements Stringifyable { +public class HsOfficePersonEntity implements HasUuid, Stringifyable { private static Stringify toString = stringify(HsOfficePersonEntity.class, "person") .withProp(Fields.personType, HsOfficePersonEntity::getPersonType) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java index 383c6853..b9b4f980 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntity.java @@ -3,8 +3,10 @@ package net.hostsharing.hsadminng.hs.office.relationship; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.stringify.Stringify; +import net.hostsharing.hsadminng.stringify.Stringifyable; import jakarta.persistence.*; import java.util.UUID; @@ -19,7 +21,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @FieldNameConstants -public class HsOfficeRelationshipEntity { +public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable { private static Stringify toString = stringify(HsOfficeRelationshipEntity.class, "rel") .withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor) @@ -27,6 +29,11 @@ public class HsOfficeRelationshipEntity { .withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder) .withProp(Fields.contact, HsOfficeRelationshipEntity::getContact); + private static Stringify toShortString = stringify(HsOfficeRelationshipEntity.class, "rel") + .withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor) + .withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType) + .withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder); + @Id @GeneratedValue private UUID uuid; @@ -51,4 +58,9 @@ public class HsOfficeRelationshipEntity { public String toString() { return toString.apply(this); } + + @Override + public String toShortString() { + return toShortString.apply(this); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java index 84f6cb3c..b4ce3a47 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java @@ -19,6 +19,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; +import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.*; @@ -48,6 +49,7 @@ import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateR import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; /* * This 'test' includes the complete legacy 'office' data import. @@ -118,6 +120,8 @@ public class ImportOfficeTables extends ContextBasedTest { private static NavigableMap coopShares = new TreeMap<>(); private static NavigableMap coopAssets = new TreeMap<>(); + private static int personId = 900000; + @PersistenceContext EntityManager em; @@ -187,24 +191,24 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { 7=partner(Mellies, Michael: Herr Michael Mellies ), - 10=partner(JM e.K.: Frau Dr. Jenny Meyer , JM e.K.), + 10=partner(JM e.K.: Herr Philip Meyer-Contract , JM e.K.), 12=partner(Test PS: Petra Schmidt , Test PS) } """); assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" { - 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), - 101=contact(label='Frau Dr. Jenny Meyer , JM e.K.', emailAddresses='jm-billing@example.org'), - 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am-operation@example.org'), - 103=contact(label='Herr Philip Meyer , JM e.K.', emailAddresses='pm-contractual@example.org'), - 121=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') + 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), + 101=contact(label='Frau Dr. Jenny Meyer-Billing , JM e.K.', emailAddresses='jm-billing@example.org'), + 102=contact(label='Herr Andrew Meyer-Operation , JM e.K.', emailAddresses='am-operation@example.org'), + 103=contact(label='Herr Philip Meyer-Contract , JM e.K.', emailAddresses='pm-contractual@example.org'), + 121=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') } """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" { - 7=person(personType='UNKNOWN', tradeName='', familyName='Mellies', givenName='Michael'), - 10=person(personType='UNKNOWN', tradeName='JM e.K.', familyName='Meyer', givenName='Jenny'), - 12=person(personType='UNKNOWN', tradeName='Test PS', familyName='Schmidt', givenName='Petra') + 900000=person(personType='UNKNOWN', tradeName='', familyName='Mellies', givenName='Michael'), + 900001=person(personType='UNKNOWN', tradeName='JM e.K.', familyName='Meyer-Contract', givenName='Philip'), + 900002=person(personType='UNKNOWN', tradeName='Test PS', familyName='Schmidt', givenName='Petra') } """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" @@ -221,6 +225,13 @@ public class ImportOfficeTables extends ContextBasedTest { 12=Membership(11012, Test PS, 1101200, [2021-04-01,), NONE) } """); + assertThat(relationships.toString()).isEqualToIgnoringWhitespace(""" + { + 71=rel(relAnchor='Mellies, Michael', relType='TECHNICAL_CONTACT', relHolder='Mellies, Michael', contact='Herr Michael Mellies '), + 102=rel(relAnchor='JM e.K.', relType='TECHNICAL_CONTACT', relHolder='JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), + 121=rel(relAnchor='Test PS', relType='TECHNICAL_CONTACT', relHolder='Test PS', contact='Petra Schmidt , Test PS') + } + """); } @Test @@ -318,60 +329,79 @@ public class ImportOfficeTables extends ContextBasedTest { jpaAttempt.transacted(() -> { context(rbacSuperuser); - contacts.forEach((id, contact) -> em.persist(contact)); + contacts.forEach(this::persist); updateLegacyIds(contacts, "hs_office_contact_legacy_id", "contact_id"); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - persons.forEach((id, person) -> em.persist(person)); + persons.forEach(this::persist); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - partners.forEach((id, partner) -> em.persist(partner)); + partners.forEach(this::persist); updateLegacyIds(partners, "hs_office_partner_legacy_id", "bp_id"); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - debitors.forEach((id, debitor) -> em.persist(debitor)); + debitors.forEach(this::persist); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - memberships.forEach((id, membership) -> em.persist(membership)); + memberships.forEach(this::persist); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - bankAccounts.forEach((id, account) -> em.persist(account)); + relationships.forEach(this::persist); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - sepaMandates.forEach((id, mandate) -> em.persist(mandate)); + bankAccounts.forEach(this::persist); + + }).assertSuccessful(); + + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + sepaMandates.forEach(this::persist); updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - coopShares.forEach((id, shareTransaction) -> em.persist(shareTransaction)); + coopShares.forEach(this::persist); updateLegacyIds(coopShares, "hs_office_coopsharestransaction_legacy_id", "member_share_id"); }).assertSuccessful(); jpaAttempt.transacted(() -> { context(rbacSuperuser); - coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction)); + coopAssets.forEach(this::persist); updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id"); }).assertSuccessful(); } + private void persist(final Integer id, final HasUuid entity) { + try { + System.out.println("persisting #" + entity.hashCode() + ": " + entity.toString()); + em.persist(entity); + em.flush(); + System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); + } catch (Exception x) { + System.out.println("failed to persist: " + entity.toString()); + throw x; + } + + } + private void deleteTestDataFromHsOfficeTables() { jpaAttempt.transacted(() -> { context(rbacSuperuser); @@ -474,12 +504,13 @@ public class ImportOfficeTables extends ContextBasedTest { final var person = HsOfficePersonEntity.builder() .personType(HsOfficePersonType.UNKNOWN) // TODO .build(); - persons.put(rec.getInteger("bp_id"), person); +// persons.put(rec.getInteger("bp_id"), person); + persons.put(personId++, person); final var partner = HsOfficePartnerEntity.builder() .debitorNumberPrefix(rec.getInteger("member_id")) .details(HsOfficePartnerDetailsEntity.builder().build()) - .contact(HsOfficeContactEntity.builder().build()) + .contact(null) // is set during contacts import depending on assigned roles .person(person) .build(); partners.put(rec.getInteger("bp_id"), partner); @@ -489,7 +520,6 @@ public class ImportOfficeTables extends ContextBasedTest { .debitorNumberSuffix((byte) 0) .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) - .billingContact(partner.getContact()) // TODO falsch .billable(rec.isEmpty("free")) .vatReverseCharge(rec.getBoolean("exempt_vat")) .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove @@ -627,7 +657,8 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - if (isNotBlank(rec.getString("roles")) && rec.getString("roles").contains("billing")) { + if (isNotBlank(rec.getString("roles"))) { + final var contactId = rec.getInteger("contact_id"); final var partner = partners.get(rec.getInteger("bp_id")); final var debitor = debitors.get(rec.getInteger("bp_id")); @@ -638,22 +669,31 @@ public class ImportOfficeTables extends ContextBasedTest { person.setGivenName(rec.getString("first_name")); person.setFamilyName(rec.getString("last_name")); - if (rec.getString("roles").contains("contractual")) { - initContact(partner.getContact(), rec); - } else if (rec.getString("roles").contains("billing")) { - initContact(debitor.getBillingContact(), rec); - } else if (rec.getString("roles").contains("operation")) { - // TODO: technical contact initContact(debitor.getBillingContact(), rec); - } + final var contact = HsOfficeContactEntity.builder().build(); + contacts.put(contactId, initContact(contact, rec)); + if (rec.getString("roles").contains("contractual")) { + partner.setContact(contact); + } + if (rec.getString("roles").contains("billing")) { + debitor.setBillingContact(contact); + } + if (rec.getString("roles").contains("operation")) { + final var rel = HsOfficeRelationshipEntity.builder() + .relAnchor(partner.getPerson()) + .relHolder(person) + .contact(contact) + .relType(HsOfficeRelationshipType.TECHNICAL_CONTACT) + .build(); + relationships.put(contactId, rel); + } } else { - initContact(new HsOfficeContactEntity(), rec); - // TODO: create relationship + fail("contact without role: " + rec); } }); } - private void initContact(final HsOfficeContactEntity contact, final Record rec) { + private HsOfficeContactEntity initContact(final HsOfficeContactEntity contact, final Record rec) { contacts.put(rec.getInteger("contact_id"), contact); contact.setLabel(toLabel( @@ -665,6 +705,7 @@ public class ImportOfficeTables extends ContextBasedTest { contact.setEmailAddresses(rec.getString("email")); contact.setPostalAddress(toAddress(rec)); contact.setPhoneNumbers(toPhoneNumbers(rec)); + return contact; } private String[] trimAll(final String[] record) { diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index 040a7c75..aec7aea7 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,6 +1,6 @@ contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zipcode;city; country; phone_private; phone_office; phone_mobile; fax; email; roles 71; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; contractual,billing,operation -101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing -102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation -103; 10; Herr; Philip; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-contractual@example.org; contractual +101; 10; Frau; Jenny; Meyer-Billing; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing +102; 10; Herr; Andrew; Meyer-Operation; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation +103; 10; Herr; Philip; Meyer-Contract; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-contractual@example.org; contractual 121; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,contractual,operation