import operational contact + allow multiple assignments of contacts

This commit is contained in:
Michael Hoennig 2024-01-16 09:53:39 +01:00
parent cd7ea891c2
commit 3a66a28a33
8 changed files with 99 additions and 41 deletions

View File

@ -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<HsOfficeBankAccountEntity> toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
.withProp(Fields.holder, HsOfficeBankAccountEntity::getHolder)

View File

@ -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)

View File

@ -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<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class)
.withProp(HsOfficeMembershipEntity::getMemberNumber)

View File

@ -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<HsOfficePartnerDetailsEntity> stringify = stringify(
HsOfficePartnerDetailsEntity.class,

View File

@ -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<HsOfficePersonEntity> toString = stringify(HsOfficePersonEntity.class, "person")
.withProp(Fields.personType, HsOfficePersonEntity::getPersonType)

View File

@ -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<HsOfficeRelationshipEntity> 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<HsOfficeRelationshipEntity> 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);
}
}

View File

@ -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<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeCoopAssetsTransactionEntity> 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) {

View File

@ -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

1 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
2 71 7 Herr Michael Mellies Kleine Freiheit 50 26524 Hage DE +49 4931 123456 +49 1522 123456 mih@example.org contractual,billing,operation
3 101 10 Frau Jenny Meyer 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
4 102 10 Herr Andrew Meyer Meyer-Operation JM e.K. Waldweg 5 11001 Berlin DE +49 30 6666666 +49 30 3333333 +49 30 4444444 am-operation@example.org operation
5 103 10 Herr Philip Meyer Meyer-Contract JM e.K. Waldweg 5 11001 Berlin DE +49 30 6666666 +49 30 5555555 +49 30 6666666 pm-contractual@example.org contractual
6 121 12 Petra Schmidt Test PS ps@example.com billing,contractual,operation