From e2a69fe103a9629407d7e150fe3166fdf9ce822e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 28 Oct 2022 11:21:55 +0200 Subject: [PATCH 01/72] csv-test-import-files --- src/test/resources/migration/asset-transactions.csv | 5 +++++ src/test/resources/migration/business_partners.csv | 4 ++++ src/test/resources/migration/contacts.csv | 5 +++++ src/test/resources/migration/sepa-mandates.csv | 3 +++ src/test/resources/migration/share-transactions.csv | 5 +++++ 5 files changed, 22 insertions(+) create mode 100644 src/test/resources/migration/asset-transactions.csv create mode 100644 src/test/resources/migration/business_partners.csv create mode 100644 src/test/resources/migration/contacts.csv create mode 100644 src/test/resources/migration/sepa-mandates.csv create mode 100644 src/test/resources/migration/share-transactions.csv diff --git a/src/test/resources/migration/asset-transactions.csv b/src/test/resources/migration/asset-transactions.csv new file mode 100644 index 00000000..d740af62 --- /dev/null +++ b/src/test/resources/migration/asset-transactions.csv @@ -0,0 +1,5 @@ +member_asstr_id; bp_id; date; action; amount +33443; 7; 2000-12-06; PAYMENT; 1280 +33451; 10; 2000-12-06; PAYMENT; 128 +33701; 7; 2005-01-10; PAYMENT; 2560 +33810; 10; 2016-12-31; PAYBACK; 128 diff --git a/src/test/resources/migration/business_partners.csv b/src/test/resources/migration/business_partners.csv new file mode 100644 index 00000000..f21cab81 --- /dev/null +++ b/src/test/resources/migration/business_partners.csv @@ -0,0 +1,4 @@ +bp_id; member_id; member_code; member_since; member_until; member_role; author_contract;nondisc_contract; free; exempt_vat; indicator_vat; uid_vat +7; 10007; mih; 2000-12-06; ; Aufsichtsrat; 2006-10-15; 2001-10-15; false; false; NET; DE-VAT-007 +10; 10010; xyz; 2000-12-06; 2015-12-31; ; ; ; false; false; GROSS; +12; 11012; xxx; 2021-04-01; ; ; ; ; true; true; GROSS; diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv new file mode 100644 index 00000000..73a16af2 --- /dev/null +++ b/src/test/resources/migration/contacts.csv @@ -0,0 +1,5 @@ +contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zipcode;city; country; phone_private; phone_office; phone_mobile; fax; email +71; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org +101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 8888888; ; +49 30 9999999; jm@example.org +102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 9999999; am@example.org +121; 12; ; Paule; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com diff --git a/src/test/resources/migration/sepa-mandates.csv b/src/test/resources/migration/sepa-mandates.csv new file mode 100644 index 00000000..a97a8750 --- /dev/null +++ b/src/test/resources/migration/sepa-mandates.csv @@ -0,0 +1,3 @@ +sepa_mandat_id; bp_id; bank_customer; bank_name; bank_iban; bank_bic; mandat_ref; mandat_signed; mandat_since; mandat_until; mandat_used +234234; 7; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 +235662; 10; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-282; 2005-07-01; ; 2016-01-18 diff --git a/src/test/resources/migration/share-transactions.csv b/src/test/resources/migration/share-transactions.csv new file mode 100644 index 00000000..de5df6af --- /dev/null +++ b/src/test/resources/migration/share-transactions.csv @@ -0,0 +1,5 @@ +member_share_id;bp_id; date; action; quantity; comment +33443; 7; 2000-12-06; SUBSCRIPTION; 20; initial share subscription +33451; 10; 2000-12-06; SUBSCRIPTION; 2; initial share subscription +33701; 7; 2005-01-10; SUBSCRIPTION; 40; increase +33810; 10; 2016-12-31; UNSUBSCRIPTION; 22; membership ended -- 2.39.2 From 545569942b09d2915479efc194c65dfb58316de3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 29 Oct 2022 10:59:03 +0200 Subject: [PATCH 02/72] import legacy hsadmin db (WIP) --- .../migration/ImportBusinessPartners.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java new file mode 100644 index 00000000..59c4a191 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -0,0 +1,78 @@ +package net.hostsharing.hsadminng.hs.office.migration; + +import com.opencsv.CSVParserBuilder; +import com.opencsv.CSVReader; +import com.opencsv.CSVReaderBuilder; +import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; +import org.junit.jupiter.api.Test; + +import jakarta.validation.constraints.NotNull; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ImportBusinessPartners { + + private Map partners = new HashMap<>(); + private Map members = new HashMap<>(); + + @Test + void importsBusinessPartners() throws Exception { + + try (Reader reader = resourceReader("migration/business_partners.csv")) { + final var records = readAllLines(reader); + + importsBusinessPartners(records); + + assertThat(records).hasSize(4); + } + + } + + public List readAllLines(Reader reader) throws Exception { + + final var parser = new CSVParserBuilder() + .withSeparator(';') + .withQuoteChar('"') + .build(); + + try (CSVReader csvReader = new CSVReaderBuilder(reader) + .withSkipLines(1) + .withCSVParser(parser) + .build()) { + return csvReader.readAll(); + } + } + + private void importsBusinessPartners(final List records) { + records.forEach( record -> { + + + HsOfficePersonEntity.builder() + .tradeName(record[]) + + .build(); + }); + } + + private Reader resourceReader(@NotNull final String resourcePath) { + return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath)); + } + + private Reader fileReader(@NotNull final Path filePath) throws IOException { + // Path path = Paths.get( + // ClassLoader.getSystemResource("csv/twoColumn.csv").toURI()) + // ); + return Files.newBufferedReader(filePath); + } + +} -- 2.39.2 From b528cf7c97ee71660c021287820622b9316fc29c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 29 Oct 2022 15:36:42 +0200 Subject: [PATCH 03/72] importing business-partners + contacts from CSV --- .../HsOfficeReasonForTermination.java | 2 +- .../hs/office/person/HsOfficePersonType.java | 1 + .../HsOfficeSepaMandateEntity.java | 1 + .../db/changelog/210-hs-office-person.sql | 2 +- .../db/changelog/300-hs-office-membership.sql | 2 +- .../migration/ImportBusinessPartners.java | 206 ++++++++++++++++-- .../resources/migration/business-partners.csv | 4 + .../resources/migration/business_partners.csv | 4 - 8 files changed, 201 insertions(+), 21 deletions(-) create mode 100644 src/test/resources/migration/business-partners.csv delete mode 100644 src/test/resources/migration/business_partners.csv diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeReasonForTermination.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeReasonForTermination.java index c043152d..a2a41051 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeReasonForTermination.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeReasonForTermination.java @@ -1,5 +1,5 @@ package net.hostsharing.hsadminng.hs.office.membership; public enum HsOfficeReasonForTermination { - NONE, CANCELLATION, TRANSFER, DEATH, LIQUIDATION, EXPULSION; + NONE, CANCELLATION, TRANSFER, DEATH, LIQUIDATION, EXPULSION, UNKNOWN; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonType.java index 589e6eeb..aae35eaf 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonType.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.office.person; public enum HsOfficePersonType { + UNKNOWN, NATURAL, LEGAL, SOLE_REPRESENTATION, diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index db31adcb..84264bd6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -54,6 +54,7 @@ public class HsOfficeSepaMandateEntity implements Stringifyable { @Column(name = "validity", columnDefinition = "daterange") @Type(PostgreSQLRangeType.class) + @Builder.Default private Range validity = Range.infinite(LocalDate.class); public void setValidFrom(final LocalDate validFrom) { diff --git a/src/main/resources/db/changelog/210-hs-office-person.sql b/src/main/resources/db/changelog/210-hs-office-person.sql index 946a205e..3f404b65 100644 --- a/src/main/resources/db/changelog/210-hs-office-person.sql +++ b/src/main/resources/db/changelog/210-hs-office-person.sql @@ -4,7 +4,7 @@ --changeset hs-office-person-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficePersonType AS ENUM ('NATURAL', 'LEGAL', 'SOLE_REPRESENTATION', 'JOINT_REPRESENTATION'); +CREATE TYPE HsOfficePersonType AS ENUM ('UNKNOWN', 'NATURAL', 'LEGAL', 'SOLE_REPRESENTATION', 'JOINT_REPRESENTATION'); CREATE CAST (character varying as HsOfficePersonType) WITH INOUT AS IMPLICIT; diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index b1b75231..09fe2e44 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -4,7 +4,7 @@ --changeset hs-office-membership-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficeReasonForTermination AS ENUM ('NONE', 'CANCELLATION', 'TRANSFER', 'DEATH', 'LIQUIDATION', 'EXPULSION'); +CREATE TYPE HsOfficeReasonForTermination AS ENUM ('NONE', 'CANCELLATION', 'TRANSFER', 'DEATH', 'LIQUIDATION', 'EXPULSION', 'UNKNOWN'); CREATE CAST (character varying as HsOfficeReasonForTermination) WITH INOUT AS IMPLICIT; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index 59c4a191..cc7d7a93 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -3,10 +3,19 @@ package net.hostsharing.hsadminng.hs.office.migration; import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; +import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; +import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; -import org.junit.jupiter.api.Test; +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; +import net.hostsharing.test.JpaAttempt; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; import jakarta.validation.constraints.NotNull; import java.io.IOException; @@ -14,27 +23,53 @@ import java.io.InputStreamReader; import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; +import java.time.LocalDate; import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.assertj.core.api.Assertions.assertThat; +import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; +@DataJpaTest +@Import({ Context.class, JpaAttempt.class }) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportBusinessPartners { - private Map partners = new HashMap<>(); - private Map members = new HashMap<>(); + private static Map partners = new HashMap<>(); + private static Map debitors = new HashMap<>(); + private static Map memberships = new HashMap<>(); @Test - void importsBusinessPartners() throws Exception { + @Order(1) + void importsBusinessPartners() { - try (Reader reader = resourceReader("migration/business_partners.csv")) { + try (Reader reader = resourceReader("migration/business-partners.csv")) { final var records = readAllLines(reader); - importsBusinessPartners(records); - - assertThat(records).hasSize(4); + } catch (Exception e) { + throw new RuntimeException(e); } + } + + @Test + @Order(2) + void importsContacts() { + + try (Reader reader = resourceReader("migration/contacts.csv")) { + final var records = readAllLines(reader); + importsContacts(records); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @AfterAll + static void afterAll() { + System.out.println(partners); + System.out.println(debitors); + System.out.println(memberships); } @@ -54,14 +89,157 @@ public class ImportBusinessPartners { } private void importsBusinessPartners(final List records) { - records.forEach( record -> { + records.stream() + .map(this::trimAll) + .forEach(record -> { + final var partner = HsOfficePartnerEntity.builder() + .details(HsOfficePartnerDetailsEntity.builder().build()) + .contact(HsOfficeContactEntity.builder().build()) + .person(HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.UNKNOWN) // TODO + .build()) + .build(); + partners.put(toInt(record[0]), partner); - HsOfficePersonEntity.builder() - .tradeName(record[]) + final var debitor = HsOfficeDebitorEntity.builder() + .partner(partner) + .debitorNumber(toInt(record[1])) + // .memberCode(record[2]) TODO + .partner(partner) + .billingContact(partner.getContact()) + // .free(toBool(record[8])) TODO + .vatBusiness("GROSS".equals(record[10])) + .vatId(record[11]) + .build(); - .build(); - }); + partners.put(toInt(record[0]), partner); + + if (isNotBlank(record[3])) { + final var membership = HsOfficeMembershipEntity.builder() + .partner(partner) + .memberNumber(toInt(record[1])) + .validity(toPostgresDateRange(localDate(record[3]), localDate(record[4]))) + .reasonForTermination( + isBlank(record[4]) + ? HsOfficeReasonForTermination.NONE + : HsOfficeReasonForTermination.UNKNOWN) // TODO + .mainDebitor(debitor) + .build(); + memberships.put(toInt(record[0]), membership); + } + }); + } + + private void importsContacts(final List records) { + records.stream() + .map(this::trimAll) + .forEach(record -> { + + final var partner = partners.get(toInt(record[1])); + + final var contact = partner.getContact(); + contact.setLabel(toLabel(record[2], record[5], record[3], record[4], record[6])); + contact.setEmailAddresses(record[16]); + contact.setPostalAddress(toAddress(record)); + contact.setPhoneNumbers(toPhoneNumbers(record)); + + final var person = partner.getPerson(); + person.setTradeName(record[6]); + // TODO: title+salutation + person.setFamilyName(record[3]); + person.setGivenName(record[4]); + }); + } + + private String[] trimAll(final String[] record) { + for (int i = 0; i < record.length; ++i) { + if (record[i] != null) { + record[i] = record[i].trim(); + } + } + return record; + } + + private String toPhoneNumbers(final String[] record) { + final var result = new StringBuilder("{\n"); + if (isNotBlank(record[12])) + result.append(" \"private\": " + "\"" + record[12] + "\",\n"); + if (isNotBlank(record[13])) + result.append(" \"office\": " + "\"" + record[13] + "\",\n"); + if (isNotBlank(record[14])) + result.append(" \"mobile\": " + "\"" + record[14] + "\",\n"); + if (isNotBlank(record[15])) + result.append(" \"fax\": " + "\"" + record[15] + "\",\n"); + return (result + "}").replace("\",\n}", "\"\n}"); + } + + private String toAddress(final String[] record) { + final var result = new StringBuilder(); + final var name = toName(record[2], record[5], record[3], record[4]); + if (isNotBlank(name)) + result.append(name + "\n"); + if (isNotBlank(record[6])) + result.append(record[6] + "\n"); + if (isNotBlank(record[7])) + result.append("c/o " + record[7] + "\n"); + if (isNotBlank(record[8])) + result.append(record[8] + "\n"); + final var zipcodeAndCity = toZipcodeAndCity(record); + if (isNotBlank(zipcodeAndCity)) + result.append(zipcodeAndCity + "\n"); + return result.toString(); + } + + private String toZipcodeAndCity(final String[] record) { + final var result = new StringBuilder(); + if (isNotBlank(record[11])) + result.append(record[11] + " "); + if (isNotBlank(record[9])) + result.append(record[9] + " "); + if (isNotBlank(record[10])) + result.append(record[10]); + return result.toString(); + } + + private String toLabel( + final String salut, + final String title, + final String firstname, + final String lastname, + final String firm) { + final var result = new StringBuilder(); + if (isNotBlank(salut)) + result.append(salut + " "); + if (isNotBlank(title)) + result.append(title + " "); + if (isNotBlank(firstname)) + result.append(firstname + " "); + if (isNotBlank(lastname)) + result.append(lastname + " "); + if (result.length() > 0 && isNotBlank(firm)) { + result.append(", " + firm); + } + return result.toString(); + } + + private String toName(final String salut, final String title, final String firstname, final String lastname) { + return toLabel(salut, title, firstname, lastname, null); + } + + private static Integer toInt(final String value) { + return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; + } + + private static Integer toInteger(final String value) { + return isNotBlank(value) ? Integer.parseInt(value.trim()) : null; + } + + private LocalDate localDate(final String dateStringNullOrBlank) { + if (isNotBlank(dateStringNullOrBlank)) { + return LocalDate.parse(dateStringNullOrBlank); + } + return null; } private Reader resourceReader(@NotNull final String resourcePath) { diff --git a/src/test/resources/migration/business-partners.csv b/src/test/resources/migration/business-partners.csv new file mode 100644 index 00000000..7934f551 --- /dev/null +++ b/src/test/resources/migration/business-partners.csv @@ -0,0 +1,4 @@ +bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat +7;10007;mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 +10;10010;xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; +12;11012;xxx;2021-04-01;;;;;true;true;GROSS; diff --git a/src/test/resources/migration/business_partners.csv b/src/test/resources/migration/business_partners.csv deleted file mode 100644 index f21cab81..00000000 --- a/src/test/resources/migration/business_partners.csv +++ /dev/null @@ -1,4 +0,0 @@ -bp_id; member_id; member_code; member_since; member_until; member_role; author_contract;nondisc_contract; free; exempt_vat; indicator_vat; uid_vat -7; 10007; mih; 2000-12-06; ; Aufsichtsrat; 2006-10-15; 2001-10-15; false; false; NET; DE-VAT-007 -10; 10010; xyz; 2000-12-06; 2015-12-31; ; ; ; false; false; GROSS; -12; 11012; xxx; 2021-04-01; ; ; ; ; true; true; GROSS; -- 2.39.2 From 5bf2dd76fe534fc146730a5ee538b263e92b1fd7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 29 Oct 2022 16:52:44 +0200 Subject: [PATCH 04/72] persist contacts, persons and partners --- .../migration/ImportBusinessPartners.java | 75 ++++++++++++++----- src/test/resources/application.yml | 4 +- src/test/resources/migration/contacts.csv | 10 +-- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index cc7d7a93..858fbdc8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -4,6 +4,7 @@ import com.opencsv.CSVParserBuilder; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import net.hostsharing.hsadminng.context.Context; +import net.hostsharing.hsadminng.context.ContextBasedTest; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; @@ -14,9 +15,14 @@ import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotNull; import java.io.IOException; import java.io.InputStreamReader; @@ -35,12 +41,23 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class ImportBusinessPartners { +public class ImportBusinessPartners extends ContextBasedTest { + private static Map contacts = new HashMap<>(); + private static Map persons = new HashMap<>(); private static Map partners = new HashMap<>(); private static Map debitors = new HashMap<>(); private static Map memberships = new HashMap<>(); + @PersistenceContext + EntityManager em; + + @Autowired + JpaAttempt jpaAttempt; + + @MockBean + HttpServletRequest request; + @Test @Order(1) void importsBusinessPartners() { @@ -65,12 +82,16 @@ public class ImportBusinessPartners { } } - @AfterAll - static void afterAll() { - System.out.println(partners); - System.out.println(debitors); - System.out.println(memberships); + @Test + @Order(10) + void persistEntities() { + jpaAttempt.transacted( () -> { + context("superuser-alex@hostsharing.net"); // TODO: use real user + contacts.forEach((id, contact) -> em.persist(contact)); + persons.forEach((id, person) -> em.persist(person)); + partners.forEach((id, partner) -> em.persist(partner)); + }); } public List readAllLines(Reader reader) throws Exception { @@ -93,12 +114,15 @@ public class ImportBusinessPartners { .map(this::trimAll) .forEach(record -> { + final var person = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.UNKNOWN) // TODO + .build(); + persons.put(toInt(record[0]), person); + final var partner = HsOfficePartnerEntity.builder() .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build()) - .person(HsOfficePersonEntity.builder() - .personType(HsOfficePersonType.UNKNOWN) // TODO - .build()) + .person(person) .build(); partners.put(toInt(record[0]), partner); @@ -136,22 +160,33 @@ public class ImportBusinessPartners { .map(this::trimAll) .forEach(record -> { - final var partner = partners.get(toInt(record[1])); + if ( isNotBlank(record[17]) && record[17].contains("billing") ) { - final var contact = partner.getContact(); - contact.setLabel(toLabel(record[2], record[5], record[3], record[4], record[6])); - contact.setEmailAddresses(record[16]); - contact.setPostalAddress(toAddress(record)); - contact.setPhoneNumbers(toPhoneNumbers(record)); + final var partner = partners.get(toInt(record[1])); - final var person = partner.getPerson(); - person.setTradeName(record[6]); - // TODO: title+salutation - person.setFamilyName(record[3]); - person.setGivenName(record[4]); + final var person = partner.getPerson(); + person.setTradeName(record[6]); + // TODO: title+salutation + person.setFamilyName(record[3]); + person.setGivenName(record[4]); + + initContact(partner.getContact(), record); + } else { + initContact(new HsOfficeContactEntity(), record); + // TODO: create relationship + } }); } + private void initContact(final HsOfficeContactEntity contact, final String[] record) { + contacts.put(toInt(record[0]), contact); + + contact.setLabel(toLabel(record[2], record[5], record[3], record[4], record[6])); + contact.setEmailAddresses(record[16]); + contact.setPostalAddress(toAddress(record)); + contact.setPhoneNumbers(toPhoneNumbers(record)); + } + private String[] trimAll(final String[] record) { for (int i = 0; i < record.length; ++i) { if (record[i] != null) { diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 9915854e..410e118c 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,8 +4,8 @@ spring: platform: postgres datasource: - url: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers - url-local: jdbc:postgresql://localhost:5432/postgres + url-tc: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers + url: jdbc:postgresql://localhost:5432/postgres username: postgres password: password diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index 73a16af2..341d22d4 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,5 +1,5 @@ -contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zipcode;city; country; phone_private; phone_office; phone_mobile; fax; email -71; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org -101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 8888888; ; +49 30 9999999; jm@example.org -102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 9999999; am@example.org -121; 12; ; Paule; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com +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; billing,operation +101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 8888888; ; +49 30 9999999; jm@example.org; billing +102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 9999999; am@example.org; operation +121; 12; ; Paule; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,operation -- 2.39.2 From fa5fb301174292a229314cab5ff308913c569890 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 29 Oct 2022 17:51:14 +0200 Subject: [PATCH 05/72] persist memberships+debitors - still with wrong reference --- .../migration/ImportBusinessPartners.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index 858fbdc8..3fd284fa 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -60,11 +60,11 @@ public class ImportBusinessPartners extends ContextBasedTest { @Test @Order(1) - void importsBusinessPartners() { + void importBusinessPartners() { try (Reader reader = resourceReader("migration/business-partners.csv")) { final var records = readAllLines(reader); - importsBusinessPartners(records); + importBusinessPartners(records); } catch (Exception e) { throw new RuntimeException(e); } @@ -72,11 +72,11 @@ public class ImportBusinessPartners extends ContextBasedTest { @Test @Order(2) - void importsContacts() { + void importContacts() { try (Reader reader = resourceReader("migration/contacts.csv")) { final var records = readAllLines(reader); - importsContacts(records); + importContacts(records); } catch (Exception e) { throw new RuntimeException(e); } @@ -91,7 +91,9 @@ public class ImportBusinessPartners extends ContextBasedTest { contacts.forEach((id, contact) -> em.persist(contact)); persons.forEach((id, person) -> em.persist(person)); partners.forEach((id, partner) -> em.persist(partner)); - }); + debitors.forEach((id, debitor) -> em.persist(debitor)); + memberships.forEach((id, membership) -> em.persist(membership)); + }).assertSuccessful(); } public List readAllLines(Reader reader) throws Exception { @@ -109,7 +111,7 @@ public class ImportBusinessPartners extends ContextBasedTest { } } - private void importsBusinessPartners(final List records) { + private void importBusinessPartners(final List records) { records.stream() .map(this::trimAll) .forEach(record -> { @@ -136,6 +138,7 @@ public class ImportBusinessPartners extends ContextBasedTest { .vatBusiness("GROSS".equals(record[10])) .vatId(record[11]) .build(); + debitors.put(toInt(record[0]), debitor); partners.put(toInt(record[0]), partner); @@ -155,7 +158,7 @@ public class ImportBusinessPartners extends ContextBasedTest { }); } - private void importsContacts(final List records) { + private void importContacts(final List records) { records.stream() .map(this::trimAll) .forEach(record -> { @@ -171,6 +174,7 @@ public class ImportBusinessPartners extends ContextBasedTest { person.setGivenName(record[4]); initContact(partner.getContact(), record); + } else { initContact(new HsOfficeContactEntity(), record); // TODO: create relationship -- 2.39.2 From d44d2e3e82c1fdbd2c5a6f9026910228da58b2bb Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 29 Oct 2022 18:18:19 +0200 Subject: [PATCH 06/72] import bank-accounts + sepa-mandates --- .../migration/ImportBusinessPartners.java | 61 ++++++++++++++++++- .../resources/migration/sepa-mandates.csv | 2 +- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index 3fd284fa..09ec4546 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -5,6 +5,7 @@ import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.ContextBasedTest; +import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; @@ -13,8 +14,12 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; 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.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.test.JpaAttempt; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -48,6 +53,8 @@ public class ImportBusinessPartners extends ContextBasedTest { private static Map partners = new HashMap<>(); private static Map debitors = new HashMap<>(); private static Map memberships = new HashMap<>(); + private static Map sepaMandates = new HashMap<>(); + private static Map bankAccounts = new HashMap<>(); @PersistenceContext EntityManager em; @@ -82,10 +89,22 @@ public class ImportBusinessPartners extends ContextBasedTest { } } + @Test + @Order(3) + void importSepaMandates() { + + try (Reader reader = resourceReader("migration/sepa-mandates.csv")) { + final var records = readAllLines(reader); + importSepaMandates(records); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + @Test @Order(10) void persistEntities() { - jpaAttempt.transacted( () -> { + jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); // TODO: use real user contacts.forEach((id, contact) -> em.persist(contact)); @@ -93,6 +112,8 @@ public class ImportBusinessPartners extends ContextBasedTest { partners.forEach((id, partner) -> em.persist(partner)); debitors.forEach((id, debitor) -> em.persist(debitor)); memberships.forEach((id, membership) -> em.persist(membership)); + bankAccounts.forEach((id, account) -> em.persist(account)); + sepaMandates.forEach((id, mandate) -> em.persist(mandate)); }).assertSuccessful(); } @@ -158,12 +179,39 @@ public class ImportBusinessPartners extends ContextBasedTest { }); } + private void importSepaMandates(final List records) { + records.stream() + .map(this::trimAll) + .forEach(record -> { + + final var debitor = debitors.get(toInt(record[1])); + + final var sepaMandate = HsOfficeSepaMandateEntity.builder() + .debitor(debitor) + .bankAccount(HsOfficeBankAccountEntity.builder() + .holder(record[2]) + // .bankName(record[3]) // TODO + .iban(record[4]) + .bic(record[5]) + .build()) + .reference(record[6]) + .agreement(LocalDate.parse(record[7])) + .validity(toPostgresDateRange( + toLocalDate(record[8]), + toLocalDate(record[9]))) + .build(); + + sepaMandates.put(toInt(record[0]), sepaMandate); + bankAccounts.put(toInt(record[0]), sepaMandate.getBankAccount()); + }); + } + private void importContacts(final List records) { records.stream() .map(this::trimAll) .forEach(record -> { - if ( isNotBlank(record[17]) && record[17].contains("billing") ) { + if (isNotBlank(record[17]) && record[17].contains("billing")) { final var partner = partners.get(toInt(record[1])); @@ -266,6 +314,13 @@ public class ImportBusinessPartners extends ContextBasedTest { return toLabel(salut, title, firstname, lastname, null); } + private LocalDate toLocalDate(final String dateString) { + if (isNotBlank(dateString)) { + return LocalDate.parse(dateString); + } + return null; + } + private static Integer toInt(final String value) { return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; } diff --git a/src/test/resources/migration/sepa-mandates.csv b/src/test/resources/migration/sepa-mandates.csv index a97a8750..9ee6720e 100644 --- a/src/test/resources/migration/sepa-mandates.csv +++ b/src/test/resources/migration/sepa-mandates.csv @@ -1,3 +1,3 @@ sepa_mandat_id; bp_id; bank_customer; bank_name; bank_iban; bank_bic; mandat_ref; mandat_signed; mandat_since; mandat_until; mandat_used 234234; 7; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 -235662; 10; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-282; 2005-07-01; ; 2016-01-18 +235662; 10; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-28; 2005-07-01; ; 2016-01-18 -- 2.39.2 From 1134401310de72ab327032533516932b8ce6ee87 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 30 Oct 2022 09:00:11 +0100 Subject: [PATCH 07/72] add some asserts --- .../migration/ImportBusinessPartners.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index 09ec4546..d295b76e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -42,6 +42,7 @@ import java.util.Map; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @@ -75,6 +76,13 @@ public class ImportBusinessPartners extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + + // no sensible content without contacts, thus just checking counts + assertThat(partners.size()).isEqualTo(3); + assertThat(contacts.size()).isEqualTo(0); + assertThat(persons.size()).isEqualTo(3); + assertThat(debitors.size()).isEqualTo(3); + assertThat(memberships.size()).isEqualTo(3); } @Test @@ -87,6 +95,43 @@ public class ImportBusinessPartners extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + + assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" + { + 7=partner(Michael, Mellies: Herr Michael Mellies ), + 10=partner(JM e.K.: Frau Dr. Jenny Meyer , JM e.K.), + 12=partner(Test PS: Paule Schmidt , Test PS) + } + """); + assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" + { + 101=contact(label='Frau Dr. Jenny Meyer , JM e.K.', emailAddresses='jm@example.org'), + 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@example.org'), + 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), + 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com') + } + """); + assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" + { + 7=person(personType='UNKNOWN', tradeName='', familyName='Michael', givenName='Mellies'), + 10=person(personType='UNKNOWN', tradeName='JM e.K.', familyName='Jenny', givenName='Meyer'), + 12=person(personType='UNKNOWN', tradeName='Test PS', familyName='Paule', givenName='Schmidt') + } + """); + assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" + { + 7=debitor(10007: Michael, Mellies), + 10=debitor(10010: JM e.K.), + 12=debitor(11012: Test PS) + } + """); + assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" + { + 7=Membership(10007, Michael, Mellies, 10007, [2000-12-06,), NONE), + 10=Membership(10010, JM e.K., 10010, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, Test PS, 11012, [2021-04-01,), NONE) + } + """); } @Test -- 2.39.2 From e1dd9b08c250ce2f492df1e7f8c7f9bae3cc5169 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 30 Oct 2022 09:14:01 +0100 Subject: [PATCH 08/72] add some asserts to sepa mandate import --- .../migration/ImportBusinessPartners.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index d295b76e..16a3570c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -44,6 +44,15 @@ import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.assertj.core.api.Assertions.assertThat; +/* + * This 'test' includes the complete legacy 'office' data import. + * + * There is no code in 'main' because the import is not needed a normal runtime. + * There is some test data in Java resources to verfiy the data conversion. + * For a real import a main method will be added later + * which reads CSV files from the file system. + */ + @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -144,6 +153,19 @@ public class ImportBusinessPartners extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + + assertThat(bankAccounts.toString()).isEqualToIgnoringWhitespace(""" + { + 234234=bankAccount(holder='Michael Mellies', iban='DE37500105177419788228', bic='INGDDEFFXXX'), + 235662=bankAccount(holder='JM e.K.', iban='DE49500105174516484892', bic='INGDDEFFXXX') + } + """); + assertThat(sepaMandates.toString()).isEqualToIgnoringWhitespace(""" + { + 234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)), + 235662=SEPA-Mandate(DE49500105174516484892, JM33344, 2005-06-28, [2005-07-01,)) + } + """); } @Test -- 2.39.2 From a4c161493903c4b317b061f336c034b1c7429262 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 30 Oct 2022 09:38:46 +0100 Subject: [PATCH 09/72] use test database --- .../migration/ImportBusinessPartners.java | 22 ++++++++++--------- src/test/resources/application.yml | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java index 16a3570c..ad4ee565 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -170,18 +171,19 @@ public class ImportBusinessPartners extends ContextBasedTest { @Test @Order(10) + @Transactional void persistEntities() { - jpaAttempt.transacted(() -> { - context("superuser-alex@hostsharing.net"); // TODO: use real user + context("superuser-alex@hostsharing.net"); // TODO: use real user for actual import - contacts.forEach((id, contact) -> em.persist(contact)); - persons.forEach((id, person) -> em.persist(person)); - partners.forEach((id, partner) -> em.persist(partner)); - debitors.forEach((id, debitor) -> em.persist(debitor)); - memberships.forEach((id, membership) -> em.persist(membership)); - bankAccounts.forEach((id, account) -> em.persist(account)); - sepaMandates.forEach((id, mandate) -> em.persist(mandate)); - }).assertSuccessful(); + contacts.forEach((id, contact) -> em.persist(contact)); + persons.forEach((id, person) -> em.persist(person)); + partners.forEach((id, partner) -> em.persist(partner)); + debitors.forEach((id, debitor) -> em.persist(debitor)); + memberships.forEach((id, membership) -> em.persist(membership)); + bankAccounts.forEach((id, account) -> em.persist(account)); + sepaMandates.forEach((id, mandate) -> em.persist(mandate)); + + em.flush(); } public List readAllLines(Reader reader) throws Exception { diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 410e118c..9915854e 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -4,8 +4,8 @@ spring: platform: postgres datasource: - url-tc: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers - url: jdbc:postgresql://localhost:5432/postgres + url: jdbc:tc:postgresql:13.7-bullseye:///spring_boot_testcontainers + url-local: jdbc:postgresql://localhost:5432/postgres username: postgres password: password -- 2.39.2 From 33fabf35e3280e373503ff378f6c65c091cafbae Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sun, 30 Oct 2022 09:38:59 +0100 Subject: [PATCH 10/72] amend architecture test --- .../hsadminng/arch/ArchitectureTest.java | 58 ++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java index cafd3a0d..a5783201 100644 --- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java @@ -30,16 +30,17 @@ public class ArchitectureTest { "..test.pac", "..context", "..generated..", - "..hs.office.person", - "..hs.office.partner", "..hs.office.bankaccount", - "..hs.office.debitor", - "..hs.office.relationship", "..hs.office.contact", - "..hs.office.sepamandate", "..hs.office.coopassets", "..hs.office.coopshares", + "..hs.office.debitor", "..hs.office.membership", + "..hs.office.migration", + "..hs.office.partner", + "..hs.office.person", + "..hs.office.relationship", + "..hs.office.sepamandate", "..errors", "..mapper", "..ping", @@ -121,14 +122,19 @@ public class ArchitectureTest { public static final ArchRule hsOfficeBankAccountPackageRule = classes() .that().resideInAPackage("..hs.office.bankaccount..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.bankaccount..", "..hs.office.sepamandate..", "..hs.office.debitor.."); + .resideInAnyPackage("..hs.office.bankaccount..", + "..hs.office.sepamandate..", + "..hs.office.debitor..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsOfficeSepaMandatePackageRule = classes() .that().resideInAPackage("..hs.office.sepamandate..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.sepamandate..", "..hs.office.debitor.."); + .resideInAnyPackage("..hs.office.sepamandate..", + "..hs.office.debitor..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") @@ -136,7 +142,10 @@ public class ArchitectureTest { .that().resideInAPackage("..hs.office.contact..") .should().onlyBeAccessed().byClassesThat() .resideInAnyPackage("..hs.office.contact..", "..hs.office.relationship..", - "..hs.office.partner..", "..hs.office.debitor..", "..hs.office.membership.."); + "..hs.office.partner..", + "..hs.office.debitor..", + "..hs.office.membership..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") @@ -144,35 +153,47 @@ public class ArchitectureTest { .that().resideInAPackage("..hs.office.person..") .should().onlyBeAccessed().byClassesThat() .resideInAnyPackage("..hs.office.person..", "..hs.office.relationship..", - "..hs.office.partner..", "..hs.office.debitor..", "..hs.office.membership.."); + "..hs.office.partner..", + "..hs.office.debitor..", + "..hs.office.membership..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsOfficeRelationshipPackageRule = classes() .that().resideInAPackage("..hs.office.relationship..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.relationship.."); + .resideInAnyPackage("..hs.office.relationship..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsOfficePartnerPackageRule = classes() .that().resideInAPackage("..hs.office.partner..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.partner..", "..hs.office.debitor..", "..hs.office.membership.."); + .resideInAnyPackage("..hs.office.partner..", + "..hs.office.debitor..", + "..hs.office.membership..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsOfficeMembershipPackageRule = classes() .that().resideInAPackage("..hs.office.membership..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.membership..", "..hs.office.coopassets..", "..hs.office.coopshares.."); + .resideInAnyPackage("..hs.office.membership..", + "..hs.office.coopassets..", + "..hs.office.coopshares..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") public static final ArchRule hsOfficeCoopAssetsPackageRule = classes() .that().resideInAPackage("..hs.office.coopassets..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.coopassets.."); + .resideInAnyPackage( + "..hs.office.coopassets..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") @@ -181,6 +202,13 @@ public class ArchitectureTest { .should().onlyBeAccessed().byClassesThat() .resideInAnyPackage("..hs.office.coopshares.."); + @ArchTest + @SuppressWarnings("unused") + public static final ArchRule hsOfficeMigrationPackageRule = classes() + .that().resideInAPackage("..hs.office.migration..") + .should().onlyBeAccessed().byClassesThat() + .resideInAnyPackage("..hs.office.migration.."); + @ArchTest @SuppressWarnings("unused") public static final ArchRule acceptsAnnotationOnMethodsRule = methods() @@ -197,7 +225,7 @@ public class ArchitectureTest { @ArchTest @SuppressWarnings("unused") - public static final ArchRule doNotUsejakartaTransactionAnnotationAtClassLevel = noClasses() + public static final ArchRule doNotUseJakartaTransactionAnnotationAtClassLevel = noClasses() .should().beAnnotatedWith(jakarta.transaction.Transactional.class.getName()) .as("Use @%s instead of @%s.".formatted( org.springframework.transaction.annotation.Transactional.class.getName(), @@ -205,7 +233,7 @@ public class ArchitectureTest { @ArchTest @SuppressWarnings("unused") - public static final ArchRule doNotUsejakartaTransactionAnnotationAtMethodLevel = noMethods() + public static final ArchRule doNotUseJakartaTransactionAnnotationAtMethodLevel = noMethods() .should().beAnnotatedWith(jakarta.transaction.Transactional.class) .as("Use @%s instead of @%s.".formatted( org.springframework.transaction.annotation.Transactional.class.getName(), -- 2.39.2 From 37e867c33dc01d5f8bc2c6876b22b5b6bf8f81ef Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 25 Nov 2022 06:30:31 +0100 Subject: [PATCH 11/72] rename ImportBusinessPartners to ImportOfficeTables --- ...sPartners.java => ImportOfficeTables.java} | 137 ++++++++++++------ 1 file changed, 94 insertions(+), 43 deletions(-) rename src/test/java/net/hostsharing/hsadminng/hs/office/migration/{ImportBusinessPartners.java => ImportOfficeTables.java} (79%) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java similarity index 79% rename from src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java rename to src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java index ad4ee565..c9119da2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportBusinessPartners.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java @@ -57,7 +57,7 @@ import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class ImportBusinessPartners extends ContextBasedTest { +public class ImportOfficeTables extends ContextBasedTest { private static Map contacts = new HashMap<>(); private static Map persons = new HashMap<>(); @@ -81,8 +81,8 @@ public class ImportBusinessPartners extends ContextBasedTest { void importBusinessPartners() { try (Reader reader = resourceReader("migration/business-partners.csv")) { - final var records = readAllLines(reader); - importBusinessPartners(records); + final var lines = readAllLines(reader); + importBusinessPartners(lines.get(0), withoutFirstElement(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -100,8 +100,8 @@ public class ImportBusinessPartners extends ContextBasedTest { void importContacts() { try (Reader reader = resourceReader("migration/contacts.csv")) { - final var records = readAllLines(reader); - importContacts(records); + final var lines = readAllLines(reader); + importContacts(lines.get(0), withoutFirstElement(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -149,8 +149,8 @@ public class ImportBusinessPartners extends ContextBasedTest { void importSepaMandates() { try (Reader reader = resourceReader("migration/sepa-mandates.csv")) { - final var records = readAllLines(reader); - importSepaMandates(records); + final var lines = readAllLines(reader); + importSepaMandates(lines.get(0), withoutFirstElement(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -194,106 +194,121 @@ public class ImportBusinessPartners extends ContextBasedTest { .build(); try (CSVReader csvReader = new CSVReaderBuilder(reader) - .withSkipLines(1) + //.withSkipLines(1) .withCSVParser(parser) .build()) { return csvReader.readAll(); } } - private void importBusinessPartners(final List records) { + private void importBusinessPartners(final String[] header, final List records) { + + final var columns = new Columns(header); + records.stream() .map(this::trimAll) - .forEach(record -> { + .forEach(row -> { + final var record = new Record(columns, row); +// 0:bp_id;1:member_id;2:member_code;3:member_since;4:member_until;5:member_role;6:author_contract;7:nondisc_contract;8:free;9:exempt_vat;10:indicator_vat;11:uid_vat final var person = HsOfficePersonEntity.builder() .personType(HsOfficePersonType.UNKNOWN) // TODO .build(); - persons.put(toInt(record[0]), person); + persons.put(toInt(record.get("bp_id")), person); final var partner = HsOfficePartnerEntity.builder() .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build()) .person(person) .build(); - partners.put(toInt(record[0]), partner); + partners.put(toInt(row[0]), partner); final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) - .debitorNumber(toInt(record[1])) + .debitorNumber(toInt(row[1])) // .memberCode(record[2]) TODO .partner(partner) .billingContact(partner.getContact()) // .free(toBool(record[8])) TODO - .vatBusiness("GROSS".equals(record[10])) - .vatId(record[11]) + .vatBusiness("GROSS".equals(row[10])) + .vatId(row[11]) .build(); - debitors.put(toInt(record[0]), debitor); + debitors.put(toInt(row[0]), debitor); - partners.put(toInt(record[0]), partner); + partners.put(toInt(row[0]), partner); - if (isNotBlank(record[3])) { + if (isNotBlank(row[3])) { final var membership = HsOfficeMembershipEntity.builder() .partner(partner) - .memberNumber(toInt(record[1])) - .validity(toPostgresDateRange(localDate(record[3]), localDate(record[4]))) + .memberNumber(toInt(row[1])) + .validity(toPostgresDateRange(localDate(row[3]), localDate(row[4]))) .reasonForTermination( - isBlank(record[4]) + isBlank(row[4]) ? HsOfficeReasonForTermination.NONE : HsOfficeReasonForTermination.UNKNOWN) // TODO .mainDebitor(debitor) .build(); - memberships.put(toInt(record[0]), membership); + memberships.put(toInt(row[0]), membership); } }); } - private void importSepaMandates(final List records) { + private void importSepaMandates(final String[] header, final List records) { + + final var columns = new Columns(header); + records.stream() .map(this::trimAll) - .forEach(record -> { + .forEach(row -> { - final var debitor = debitors.get(toInt(record[1])); + final var record = new Record(columns, row); + + final var debitor = debitors.get(toInt(row[1])); final var sepaMandate = HsOfficeSepaMandateEntity.builder() .debitor(debitor) .bankAccount(HsOfficeBankAccountEntity.builder() - .holder(record[2]) + .holder(row[2]) // .bankName(record[3]) // TODO - .iban(record[4]) - .bic(record[5]) + .iban(row[4]) + .bic(row[5]) .build()) - .reference(record[6]) - .agreement(LocalDate.parse(record[7])) + .reference(row[6]) + .agreement(LocalDate.parse(row[7])) .validity(toPostgresDateRange( - toLocalDate(record[8]), - toLocalDate(record[9]))) + toLocalDate(row[8]), + toLocalDate(row[9]))) .build(); - sepaMandates.put(toInt(record[0]), sepaMandate); - bankAccounts.put(toInt(record[0]), sepaMandate.getBankAccount()); + sepaMandates.put(toInt(row[0]), sepaMandate); + bankAccounts.put(toInt(row[0]), sepaMandate.getBankAccount()); }); } - private void importContacts(final List records) { + private void importContacts(final String[] header, final List records) { + + final var columns = new Columns(header); + records.stream() .map(this::trimAll) - .forEach(record -> { + .forEach(row -> { - if (isNotBlank(record[17]) && record[17].contains("billing")) { + final var record = new Record(columns, row); - final var partner = partners.get(toInt(record[1])); + if (isNotBlank(row[17]) && row[17].contains("billing")) { + + final var partner = partners.get(toInt(row[1])); final var person = partner.getPerson(); - person.setTradeName(record[6]); + person.setTradeName(row[6]); // TODO: title+salutation - person.setFamilyName(record[3]); - person.setGivenName(record[4]); + person.setFamilyName(row[3]); + person.setGivenName(row[4]); - initContact(partner.getContact(), record); + initContact(partner.getContact(), row); } else { - initContact(new HsOfficeContactEntity(), record); + initContact(new HsOfficeContactEntity(), row); // TODO: create relationship } }); @@ -416,4 +431,40 @@ public class ImportBusinessPartners extends ContextBasedTest { return Files.newBufferedReader(filePath); } + private List withoutFirstElement(final List records) { + return records.subList(1, records.size()-1); + } + +} + +class Columns { + + private final List columnNames; + + public Columns(final String[] header) { + columnNames = List.of(header); + } + + int indexOf(final String columnName) { + int index = columnNames.indexOf(columnName); + if ( index < 0 ) { + throw new RuntimeException("column name '" + columnName + "' not found in: " + columnName); + } + return index; + } +} + +class Record { + + private final Columns columns; + private final String[] row; + + public Record(final Columns columns, final String[] row) { + this.columns = columns; + this.row = row; + } + + String get(final String columnName) { + return row[columns.indexOf(columnName)]; + } } -- 2.39.2 From e8e898d18d0181d758ae3435ef05791d9ecf1734 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 4 Sep 2023 16:18:35 +0200 Subject: [PATCH 12/72] header handling for buisoness partners import file --- .../hs/office/migration/ImportOfficeTables.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 c9119da2..00eaf1fa 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 @@ -82,7 +82,7 @@ public class ImportOfficeTables extends ContextBasedTest { try (Reader reader = resourceReader("migration/business-partners.csv")) { final var lines = readAllLines(reader); - importBusinessPartners(lines.get(0), withoutFirstElement(lines)); + importBusinessPartners(justHeader(lines), withoutHeader(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -101,7 +101,7 @@ public class ImportOfficeTables extends ContextBasedTest { try (Reader reader = resourceReader("migration/contacts.csv")) { final var lines = readAllLines(reader); - importContacts(lines.get(0), withoutFirstElement(lines)); + importContacts(justHeader(lines), withoutHeader(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -150,7 +150,7 @@ public class ImportOfficeTables extends ContextBasedTest { try (Reader reader = resourceReader("migration/sepa-mandates.csv")) { final var lines = readAllLines(reader); - importSepaMandates(lines.get(0), withoutFirstElement(lines)); + importSepaMandates(justHeader(lines), withoutHeader(lines)); } catch (Exception e) { throw new RuntimeException(e); } @@ -431,7 +431,11 @@ public class ImportOfficeTables extends ContextBasedTest { return Files.newBufferedReader(filePath); } - private List withoutFirstElement(final List records) { + private static String[] justHeader(final List lines) { + return lines.get(0); + } + + private List withoutHeader(final List records) { return records.subList(1, records.size()-1); } -- 2.39.2 From a5d1544bacc2d2ad226623cae4fcf36c4a183cce Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 5 Jan 2024 10:34:41 +0100 Subject: [PATCH 13/72] fix typo member_asstr_id->member_asset_id --- src/test/resources/migration/asset-transactions.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/resources/migration/asset-transactions.csv b/src/test/resources/migration/asset-transactions.csv index d740af62..5d284e9e 100644 --- a/src/test/resources/migration/asset-transactions.csv +++ b/src/test/resources/migration/asset-transactions.csv @@ -1,4 +1,4 @@ -member_asstr_id; bp_id; date; action; amount +member_asset_id; bp_id; date; action; amount 33443; 7; 2000-12-06; PAYMENT; 1280 33451; 10; 2000-12-06; PAYMENT; 128 33701; 7; 2005-01-10; PAYMENT; 2560 -- 2.39.2 From ae1b6b44b6f1c838e1e76e8c75692c140ac9b5d6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 5 Jan 2024 13:57:01 +0100 Subject: [PATCH 14/72] fix partner import, the last row was skipped --- .../hsadminng/hs/office/migration/ImportOfficeTables.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 00eaf1fa..ac221907 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 @@ -436,7 +436,7 @@ public class ImportOfficeTables extends ContextBasedTest { } private List withoutHeader(final List records) { - return records.subList(1, records.size()-1); + return records.subList(1, records.size()); } } -- 2.39.2 From a042eff8bc745f5ce741365ba5310cc8c79360f3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 5 Jan 2024 14:20:41 +0100 Subject: [PATCH 15/72] improve test assertions --- .../office/migration/ImportOfficeTables.java | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) 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 ac221907..ff7273e8 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 @@ -87,12 +87,28 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - // no sensible content without contacts, thus just checking counts - assertThat(partners.size()).isEqualTo(3); - assertThat(contacts.size()).isEqualTo(0); - assertThat(persons.size()).isEqualTo(3); - assertThat(debitors.size()).isEqualTo(3); - assertThat(memberships.size()).isEqualTo(3); + // no contacts yet => mostly null values + assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" + { + 7=partner(null, null: null), + 10=partner(null, null: null), + 12=partner(null, null: null) + } + """); + assertThat(contacts.toString()).isEqualTo("{}"); + assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" + { + 7=debitor(10007: null, null), + 10=debitor(10010: null, null), + 12=debitor(11012: null, null)} + """); + assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" + { + 7=Membership(10007, null, null, 10007, [2000-12-06,), NONE), + 10=Membership(10010, null, null, 10010, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, null, null, 11012, [2021-04-01,), NONE) + } + """); } @Test @@ -118,7 +134,7 @@ public class ImportOfficeTables extends ContextBasedTest { 101=contact(label='Frau Dr. Jenny Meyer , JM e.K.', emailAddresses='jm@example.org'), 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@example.org'), 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), - 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com') + 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com') } """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" -- 2.39.2 From 1b3eef0e14cc9bc6c61903d9635f5bdcd5e84525 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 5 Jan 2024 14:39:00 +0100 Subject: [PATCH 16/72] use record[named-field] for column access --- .../office/migration/ImportOfficeTables.java | 168 +++++++++--------- 1 file changed, 83 insertions(+), 85 deletions(-) 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 ff7273e8..f1933005 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 @@ -36,10 +36,12 @@ import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -124,7 +126,7 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { - 7=partner(Michael, Mellies: Herr Michael Mellies ), + 7=partner(Mellies, Michael: Herr Michael Mellies ), 10=partner(JM e.K.: Frau Dr. Jenny Meyer , JM e.K.), 12=partner(Test PS: Paule Schmidt , Test PS) } @@ -139,21 +141,21 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" { - 7=person(personType='UNKNOWN', tradeName='', familyName='Michael', givenName='Mellies'), - 10=person(personType='UNKNOWN', tradeName='JM e.K.', familyName='Jenny', givenName='Meyer'), - 12=person(personType='UNKNOWN', tradeName='Test PS', familyName='Paule', givenName='Schmidt') + 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='Paule') } """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(10007: Michael, Mellies), + 7=debitor(10007: Mellies, Michael), 10=debitor(10010: JM e.K.), 12=debitor(11012: Test PS) } """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, Michael, Mellies, 10007, [2000-12-06,), NONE), + 7=Membership(10007, Mellies, Michael, 10007, [2000-12-06,), NONE), 10=Membership(10010, JM e.K., 10010, [2000-12-06,2016-01-01), UNKNOWN), 12=Membership(11012, Test PS, 11012, [2021-04-01,), NONE) } @@ -210,7 +212,6 @@ public class ImportOfficeTables extends ContextBasedTest { .build(); try (CSVReader csvReader = new CSVReaderBuilder(reader) - //.withSkipLines(1) .withCSVParser(parser) .build()) { return csvReader.readAll(); @@ -223,48 +224,50 @@ public class ImportOfficeTables extends ContextBasedTest { records.stream() .map(this::trimAll) - .forEach(row -> { - - final var record = new Record(columns, row); -// 0:bp_id;1:member_id;2:member_code;3:member_since;4:member_until;5:member_role;6:author_contract;7:nondisc_contract;8:free;9:exempt_vat;10:indicator_vat;11:uid_vat + .map(row -> new Record(columns, row)) + .forEach(rec -> { final var person = HsOfficePersonEntity.builder() .personType(HsOfficePersonType.UNKNOWN) // TODO .build(); - persons.put(toInt(record.get("bp_id")), person); + persons.put(toInt(rec.get("bp_id")), person); final var partner = HsOfficePartnerEntity.builder() .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build()) .person(person) .build(); - partners.put(toInt(row[0]), partner); + partners.put(toInt(rec.get("bp_id")), partner); final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) - .debitorNumber(toInt(row[1])) - // .memberCode(record[2]) TODO + .debitorNumber(toInt(rec.get("member_id"))) + // .memberCode(rec.get("member_code")) TODO .partner(partner) .billingContact(partner.getContact()) - // .free(toBool(record[8])) TODO - .vatBusiness("GROSS".equals(row[10])) - .vatId(row[11]) + // .memberRoles(toBool(rec.get("member_role")) TODO + // .authorContract(toBool(rec.get("author_contract")) TODO + // .nonDisclosureContract(toBool(rec.get("nondisc_contract")) TODO + // .free(toBool(rec.get("free")) TODO + // .vatExempt(toBool(rec.get("exempt_vat")) TODO + .vatBusiness("GROSS".equals(rec.get("indicator_vat"))) + .vatId(rec.get("uid_vat")) .build(); - debitors.put(toInt(row[0]), debitor); + debitors.put(toInt(rec.get("bp_id")), debitor); - partners.put(toInt(row[0]), partner); + partners.put(toInt(rec.get("bp_id")), partner); - if (isNotBlank(row[3])) { + if (isNotBlank(rec.get("member_since"))) { final var membership = HsOfficeMembershipEntity.builder() .partner(partner) - .memberNumber(toInt(row[1])) - .validity(toPostgresDateRange(localDate(row[3]), localDate(row[4]))) + .memberNumber(toInt(rec.get("member_id"))) + .validity(toPostgresDateRange(localDate(rec.get("member_since")), localDate(rec.get("member_until")))) .reasonForTermination( - isBlank(row[4]) + isBlank(rec.get("member_until")) ? HsOfficeReasonForTermination.NONE : HsOfficeReasonForTermination.UNKNOWN) // TODO .mainDebitor(debitor) .build(); - memberships.put(toInt(row[0]), membership); + memberships.put(toInt(rec.get("bp_id")), membership); } }); } @@ -275,29 +278,27 @@ public class ImportOfficeTables extends ContextBasedTest { records.stream() .map(this::trimAll) - .forEach(row -> { - - final var record = new Record(columns, row); - - final var debitor = debitors.get(toInt(row[1])); + .map(row -> new Record(columns, row)) + .forEach(rec -> { + final var debitor = debitors.get(toInt(rec.get("bp_id"))); final var sepaMandate = HsOfficeSepaMandateEntity.builder() .debitor(debitor) .bankAccount(HsOfficeBankAccountEntity.builder() - .holder(row[2]) - // .bankName(record[3]) // TODO - .iban(row[4]) - .bic(row[5]) + .holder(rec.get("bank_customer")) + // .bankName(rec.get("bank_name")) // TODO + .iban(rec.get("bank_iban")) + .bic(rec.get("bank_bic")) .build()) - .reference(row[6]) - .agreement(LocalDate.parse(row[7])) + .reference(rec.get("mandat_ref")) + .agreement(LocalDate.parse(rec.get("mandat_signed"))) .validity(toPostgresDateRange( - toLocalDate(row[8]), - toLocalDate(row[9]))) + toLocalDate(rec.get("mandat_since")), + toLocalDate(rec.get("mandat_until")))) .build(); - sepaMandates.put(toInt(row[0]), sepaMandate); - bankAccounts.put(toInt(row[0]), sepaMandate.getBankAccount()); + sepaMandates.put(toInt(rec.get("sepa_mandat_id")), sepaMandate); + bankAccounts.put(toInt(rec.get("sepa_mandat_id")), sepaMandate.getBankAccount()); }); } @@ -307,36 +308,34 @@ public class ImportOfficeTables extends ContextBasedTest { records.stream() .map(this::trimAll) - .forEach(row -> { + .map(row -> new Record(columns, row)) + .forEach(rec -> { + if (isNotBlank(rec.get("roles")) && rec.get("roles").contains("billing")) { - final var record = new Record(columns, row); - - if (isNotBlank(row[17]) && row[17].contains("billing")) { - - final var partner = partners.get(toInt(row[1])); + final var partner = partners.get(toInt(rec.get("bp_id"))); final var person = partner.getPerson(); - person.setTradeName(row[6]); + person.setTradeName(rec.get("firma")); // TODO: title+salutation - person.setFamilyName(row[3]); - person.setGivenName(row[4]); + person.setGivenName(rec.get("first_name")); + person.setFamilyName(rec.get("last_name")); - initContact(partner.getContact(), row); + initContact(partner.getContact(), rec); } else { - initContact(new HsOfficeContactEntity(), row); + initContact(new HsOfficeContactEntity(), rec); // TODO: create relationship } }); } - private void initContact(final HsOfficeContactEntity contact, final String[] record) { - contacts.put(toInt(record[0]), contact); + private void initContact(final HsOfficeContactEntity contact, final Record rec) { + contacts.put(toInt(rec.get("contact_id")), contact); - contact.setLabel(toLabel(record[2], record[5], record[3], record[4], record[6])); - contact.setEmailAddresses(record[16]); - contact.setPostalAddress(toAddress(record)); - contact.setPhoneNumbers(toPhoneNumbers(record)); + contact.setLabel(toLabel(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name"), rec.get("firma"))); + contact.setEmailAddresses(rec.get("email")); + contact.setPostalAddress(toAddress(rec)); + contact.setPhoneNumbers(toPhoneNumbers(rec)); } private String[] trimAll(final String[] record) { @@ -347,45 +346,44 @@ public class ImportOfficeTables extends ContextBasedTest { } return record; } - - private String toPhoneNumbers(final String[] record) { + private String toPhoneNumbers(final Record rec) { final var result = new StringBuilder("{\n"); - if (isNotBlank(record[12])) - result.append(" \"private\": " + "\"" + record[12] + "\",\n"); - if (isNotBlank(record[13])) - result.append(" \"office\": " + "\"" + record[13] + "\",\n"); - if (isNotBlank(record[14])) - result.append(" \"mobile\": " + "\"" + record[14] + "\",\n"); - if (isNotBlank(record[15])) - result.append(" \"fax\": " + "\"" + record[15] + "\",\n"); + if (isNotBlank(rec.get("phone_private"))) + result.append(" \"private\": " + "\"" + rec.get("phone_private") + "\",\n"); + if (isNotBlank(rec.get("phone_office"))) + result.append(" \"office\": " + "\"" + rec.get("phone_office") + "\",\n"); + if (isNotBlank(rec.get("phone_mobile"))) + result.append(" \"mobile\": " + "\"" + rec.get("phone_mobile") + "\",\n"); + if (isNotBlank(rec.get("fax"))) + result.append(" \"fax\": " + "\"" + rec.get("fax") + "\",\n"); return (result + "}").replace("\",\n}", "\"\n}"); } - private String toAddress(final String[] record) { + private String toAddress(final Record rec) { final var result = new StringBuilder(); - final var name = toName(record[2], record[5], record[3], record[4]); + final var name = toName(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name")); if (isNotBlank(name)) result.append(name + "\n"); - if (isNotBlank(record[6])) - result.append(record[6] + "\n"); - if (isNotBlank(record[7])) - result.append("c/o " + record[7] + "\n"); - if (isNotBlank(record[8])) - result.append(record[8] + "\n"); - final var zipcodeAndCity = toZipcodeAndCity(record); + if (isNotBlank(rec.get("firma"))) + result.append(rec.get("firma") + "\n"); + if (isNotBlank(rec.get("co"))) + result.append("c/o " + rec.get("co") + "\n"); + if (isNotBlank(rec.get("street"))) + result.append(rec.get("street") + "\n"); + final var zipcodeAndCity = toZipcodeAndCity(rec); if (isNotBlank(zipcodeAndCity)) result.append(zipcodeAndCity + "\n"); return result.toString(); } - private String toZipcodeAndCity(final String[] record) { + private String toZipcodeAndCity(final Record rec) { final var result = new StringBuilder(); - if (isNotBlank(record[11])) - result.append(record[11] + " "); - if (isNotBlank(record[9])) - result.append(record[9] + " "); - if (isNotBlank(record[10])) - result.append(record[10]); + if (isNotBlank(rec.get("country"))) + result.append(rec.get("country") + " "); + if (isNotBlank(rec.get("zipcode"))) + result.append(rec.get("zipcode") + " "); + if (isNotBlank(rec.get("city"))) + result.append(rec.get("city")); return result.toString(); } @@ -448,7 +446,7 @@ public class ImportOfficeTables extends ContextBasedTest { } private static String[] justHeader(final List lines) { - return lines.get(0); + return stream(lines.getFirst()).map(String::trim).toArray(String[]::new); } private List withoutHeader(final List records) { @@ -468,7 +466,7 @@ class Columns { int indexOf(final String columnName) { int index = columnNames.indexOf(columnName); if ( index < 0 ) { - throw new RuntimeException("column name '" + columnName + "' not found in: " + columnName); + throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); } return index; } -- 2.39.2 From 97f428055e0885583cc2bed7d67702421eba902d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 6 Jan 2024 18:07:09 +0100 Subject: [PATCH 17/72] import legacy-ids for contact, partner + sepamandate --- .../office/contact/HsOfficeContactEntity.java | 3 +- .../office/partner/HsOfficePartnerEntity.java | 3 +- .../HsOfficeSepaMandateEntity.java | 3 +- .../hsadminng/repository/HasUuid.java | 7 ++++ .../office/migration/ImportOfficeTables.java | 42 ++++++++++++++++--- 5 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java index 7a2c84e0..ccfbaf5f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; @@ -21,7 +22,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @AllArgsConstructor @FieldNameConstants @DisplayName("Contact") -public class HsOfficeContactEntity implements Stringifyable { +public class HsOfficeContactEntity implements Stringifyable, HasUuid { private static Stringify toString = stringify(HsOfficeContactEntity.class, "contact") .withProp(Fields.label, HsOfficeContactEntity::getLabel) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java index 86b27c8d..2dab9e55 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java @@ -4,6 +4,7 @@ import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.NotFound; @@ -22,7 +23,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("Partner") -public class HsOfficePartnerEntity implements Stringifyable { +public class HsOfficePartnerEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficePartnerEntity.class, "partner") .withProp(HsOfficePartnerEntity::getPerson) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index 84264bd6..aefc34c4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -6,6 +6,7 @@ import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.Type; @@ -25,7 +26,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("SEPA-Mandate") -public class HsOfficeSepaMandateEntity implements Stringifyable { +public class HsOfficeSepaMandateEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeSepaMandateEntity.class) .withProp(e -> e.getBankAccount().getIban()) diff --git a/src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java b/src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java new file mode 100644 index 00000000..69db49cb --- /dev/null +++ b/src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java @@ -0,0 +1,7 @@ +package net.hostsharing.hsadminng.repository; + +import java.util.UUID; + +public interface HasUuid { + UUID getUuid(); +} 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 f1933005..ef2281b1 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 @@ -15,6 +15,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.sepamandate.HsOfficeSepaMandateEntity; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -24,7 +25,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.Commit; +import org.springframework.test.annotation.Rollback; +import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -36,7 +43,6 @@ import java.io.Reader; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -72,6 +78,9 @@ public class ImportOfficeTables extends ContextBasedTest { @PersistenceContext EntityManager em; + @Autowired + TransactionTemplate txTemplate; + @Autowired JpaAttempt jpaAttempt; @@ -189,21 +198,46 @@ public class ImportOfficeTables extends ContextBasedTest { @Test @Order(10) - @Transactional + @Commit + //@Rollback(false) void persistEntities() { + context("superuser-alex@hostsharing.net"); // TODO: use real user for actual import contacts.forEach((id, contact) -> em.persist(contact)); + updateLegacyIds(contacts, "hs_office_contact_legacy_id", "contact_id"); + persons.forEach((id, person) -> em.persist(person)); + partners.forEach((id, partner) -> em.persist(partner)); + updateLegacyIds(partners, "hs_office_partner_legacy_id", "bp_id"); + debitors.forEach((id, debitor) -> em.persist(debitor)); memberships.forEach((id, membership) -> em.persist(membership)); bankAccounts.forEach((id, account) -> em.persist(account)); + sepaMandates.forEach((id, mandate) -> em.persist(mandate)); + updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); + + // TODO: coopshares+coopassets em.flush(); } + private void updateLegacyIds(Map entities, final String legacyIdTable, final String legacyIdColumn) { + entities.forEach((id, entity) -> em.createNativeQuery(""" + UPDATE ${legacyIdTable} + SET ${legacyIdColumn} = :legacyId + WHERE uuid = :uuid + """ + .replace("${legacyIdTable}", legacyIdTable) + .replace("${legacyIdColumn}", legacyIdColumn)) + .setParameter("legacyId", id) + .setParameter("uuid", entity.getUuid()) + .executeUpdate() + ); + } + public List readAllLines(Reader reader) throws Exception { final var parser = new CSVParserBuilder() @@ -423,10 +457,6 @@ public class ImportOfficeTables extends ContextBasedTest { return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; } - private static Integer toInteger(final String value) { - return isNotBlank(value) ? Integer.parseInt(value.trim()) : null; - } - private LocalDate localDate(final String dateStringNullOrBlank) { if (isNotBlank(dateStringNullOrBlank)) { return LocalDate.parse(dateStringNullOrBlank); -- 2.39.2 From abbb61727b56aa4eca1a89786bd7732669f69941 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 6 Jan 2024 18:08:29 +0100 Subject: [PATCH 18/72] fix spelling of semamandate --- .../db/changelog/256-hs-office-sepamandate-migration.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql b/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql index fe43706c..4b483c6b 100644 --- a/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql +++ b/src/main/resources/db/changelog/256-hs-office-sepamandate-migration.sql @@ -10,7 +10,7 @@ CREATE TABLE hs_office_sepamandate_legacy_id ( uuid uuid NOT NULL REFERENCES hs_office_sepamandate(uuid), - sepa_mandat_id integer NOT NULL + sepa_mandate_id integer NOT NULL ); --// @@ -22,7 +22,7 @@ CREATE TABLE hs_office_sepamandate_legacy_id CREATE SEQUENCE IF NOT EXISTS hs_office_sepamandate_legacy_id_seq AS integer START 1000000000 - OWNED BY hs_office_sepamandate_legacy_id.sepa_mandat_id; + OWNED BY hs_office_sepamandate_legacy_id.sepa_mandate_id; --// @@ -31,7 +31,7 @@ CREATE SEQUENCE IF NOT EXISTS hs_office_sepamandate_legacy_id_seq -- ---------------------------------------------------------------------------- ALTER TABLE hs_office_sepamandate_legacy_id - ALTER COLUMN sepa_mandat_id + ALTER COLUMN sepa_mandate_id SET DEFAULT nextVal('hs_office_sepamandate_legacy_id_seq'); --/ @@ -42,7 +42,7 @@ ALTER TABLE hs_office_sepamandate_legacy_id -- ---------------------------------------------------------------------------- CALL defineContext('schema-migration'); -INSERT INTO hs_office_sepamandate_legacy_id(uuid, sepa_mandat_id) +INSERT INTO hs_office_sepamandate_legacy_id(uuid, sepa_mandate_id) SELECT uuid, nextVal('hs_office_sepamandate_legacy_id_seq') FROM hs_office_sepamandate; --/ -- 2.39.2 From b0bfb127b6f2fb8f5aaf20e6376043963a87baea Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jan 2024 11:36:47 +0100 Subject: [PATCH 19/72] implement import of coop-share-transactions --- .../office/migration/ImportOfficeTables.java | 216 +++++++++++------- 1 file changed, 134 insertions(+), 82 deletions(-) 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 ef2281b1..3db38f49 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 @@ -7,6 +7,9 @@ import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.ContextBasedTest; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; +import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; +import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionEntity; +import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionType; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination; @@ -26,12 +29,7 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.Commit; -import org.springframework.test.annotation.Rollback; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.ReflectionUtils; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; @@ -74,6 +72,8 @@ public class ImportOfficeTables extends ContextBasedTest { private static Map memberships = new HashMap<>(); private static Map sepaMandates = new HashMap<>(); private static Map bankAccounts = new HashMap<>(); + private static Map coopShares = new HashMap<>(); + private static Map assetTransactions = new HashMap<>(); @PersistenceContext EntityManager em; @@ -196,6 +196,27 @@ public class ImportOfficeTables extends ContextBasedTest { """); } + @Test + @Order(4) + void importCoopShares() { + + try (Reader reader = resourceReader("migration/share-transactions.csv")) { + final var lines = readAllLines(reader); + importCoopShares(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + assertThat(coopShares.toString()).isEqualToIgnoringWhitespace(""" + { + 33810=CoopShareTransaction(10010, 2016-12-31, CANCELLATION, 22, membership ended), + 33443=CoopShareTransaction(10007, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), + 33701=CoopShareTransaction(10007, 2005-01-10, SUBSCRIPTION, 40, increase), + 33451=CoopShareTransaction(10010, 2000-12-06, SUBSCRIPTION, 2, initial share subscription) + } + """); + } + @Test @Order(10) @Commit @@ -263,49 +284,77 @@ public class ImportOfficeTables extends ContextBasedTest { final var person = HsOfficePersonEntity.builder() .personType(HsOfficePersonType.UNKNOWN) // TODO .build(); - persons.put(toInt(rec.get("bp_id")), person); + persons.put(rec.getInteger("bp_id"), person); final var partner = HsOfficePartnerEntity.builder() .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build()) .person(person) .build(); - partners.put(toInt(rec.get("bp_id")), partner); + partners.put(rec.getInteger("bp_id"), partner); final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) - .debitorNumber(toInt(rec.get("member_id"))) - // .memberCode(rec.get("member_code")) TODO + .debitorNumber(rec.getInteger("member_id")) + // .memberCode(rec.get("member_code")) TODO: add as debitor_default_prefix to debitor .partner(partner) .billingContact(partner.getContact()) - // .memberRoles(toBool(rec.get("member_role")) TODO - // .authorContract(toBool(rec.get("author_contract")) TODO - // .nonDisclosureContract(toBool(rec.get("nondisc_contract")) TODO - // .free(toBool(rec.get("free")) TODO - // .vatExempt(toBool(rec.get("exempt_vat")) TODO - .vatBusiness("GROSS".equals(rec.get("indicator_vat"))) - .vatId(rec.get("uid_vat")) + // .memberRoles(toBool(rec.get("member_role")) TODO: add negated as membership_billable to membership + // .authorContract(toBool(rec.get("author_contract")) not supported + // .nonDisclosureContract(toBool(rec.get("nondisc_contract")) not supported + // .free(toBool(rec.get("free")) TODO: add negated as billable to debitor + // .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor + .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove + .vatId(rec.getString("uid_vat")) .build(); - debitors.put(toInt(rec.get("bp_id")), debitor); + debitors.put(rec.getInteger("bp_id"), debitor); - partners.put(toInt(rec.get("bp_id")), partner); + partners.put(rec.getInteger("bp_id"), partner); - if (isNotBlank(rec.get("member_since"))) { + if (isNotBlank(rec.getString("member_since"))) { final var membership = HsOfficeMembershipEntity.builder() .partner(partner) - .memberNumber(toInt(rec.get("member_id"))) - .validity(toPostgresDateRange(localDate(rec.get("member_since")), localDate(rec.get("member_until")))) + .memberNumber(rec.getInteger("member_id")) + .validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) .reasonForTermination( - isBlank(rec.get("member_until")) + isBlank(rec.getString("member_until")) ? HsOfficeReasonForTermination.NONE : HsOfficeReasonForTermination.UNKNOWN) // TODO .mainDebitor(debitor) .build(); - memberships.put(toInt(rec.get("bp_id")), membership); + memberships.put(rec.getInteger("bp_id"), membership); } }); } + private void importCoopShares(final String[] header, final List records) { + + final var columns = new Columns(header); + + records.stream() + .map(this::trimAll) + .map(row -> new Record(columns, row)) + .forEach(rec -> { + final var member = memberships.get(rec.getInteger("bp_id")); + + final var shareTransaction = HsOfficeCoopSharesTransactionEntity.builder() + .membership(member) + .valueDate(rec.getLocalDate("date")) + .transactionType( + "SUBSCRIPTION".equals(rec.getString("action")) + ? HsOfficeCoopSharesTransactionType.SUBSCRIPTION + : "UNSUBSCRIPTION".equals(rec.getString("action")) + ? HsOfficeCoopSharesTransactionType.CANCELLATION + : HsOfficeCoopSharesTransactionType.ADJUSTMENT + ) + .shareCount(rec.getInteger("quantity")) + .reference(rec.getString("comment")) + .build(); + + coopShares.put(rec.getInteger("member_share_id"), shareTransaction); + }); + } + private void importSepaMandates(final String[] header, final List records) { final var columns = new Columns(header); @@ -314,25 +363,25 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - final var debitor = debitors.get(toInt(rec.get("bp_id"))); + final var debitor = debitors.get(rec.getInteger("bp_id")); final var sepaMandate = HsOfficeSepaMandateEntity.builder() .debitor(debitor) .bankAccount(HsOfficeBankAccountEntity.builder() - .holder(rec.get("bank_customer")) - // .bankName(rec.get("bank_name")) // TODO - .iban(rec.get("bank_iban")) - .bic(rec.get("bank_bic")) + .holder(rec.getString("bank_customer")) + // .bankName(rec.get("bank_name")) // not supported + .iban(rec.getString("bank_iban")) + .bic(rec.getString("bank_bic")) .build()) - .reference(rec.get("mandat_ref")) - .agreement(LocalDate.parse(rec.get("mandat_signed"))) + .reference(rec.getString("mandat_ref")) + .agreement(LocalDate.parse(rec.getString("mandat_signed"))) .validity(toPostgresDateRange( - toLocalDate(rec.get("mandat_since")), - toLocalDate(rec.get("mandat_until")))) + rec.getLocalDate("mandat_since"), + rec.getLocalDate("mandat_until"))) .build(); - sepaMandates.put(toInt(rec.get("sepa_mandat_id")), sepaMandate); - bankAccounts.put(toInt(rec.get("sepa_mandat_id")), sepaMandate.getBankAccount()); + sepaMandates.put(rec.getInteger("sepa_mandat_id"), sepaMandate); + bankAccounts.put(rec.getInteger("sepa_mandat_id"), sepaMandate.getBankAccount()); }); } @@ -344,15 +393,15 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - if (isNotBlank(rec.get("roles")) && rec.get("roles").contains("billing")) { + if (isNotBlank(rec.getString("roles")) && rec.getString("roles").contains("billing")) { - final var partner = partners.get(toInt(rec.get("bp_id"))); + final var partner = partners.get(rec.getInteger("bp_id")); final var person = partner.getPerson(); - person.setTradeName(rec.get("firma")); - // TODO: title+salutation - person.setGivenName(rec.get("first_name")); - person.setFamilyName(rec.get("last_name")); + person.setTradeName(rec.getString("firma")); + // TODO: title+salutation: add to person + person.setGivenName(rec.getString("first_name")); + person.setFamilyName(rec.getString("last_name")); initContact(partner.getContact(), rec); @@ -364,10 +413,10 @@ public class ImportOfficeTables extends ContextBasedTest { } private void initContact(final HsOfficeContactEntity contact, final Record rec) { - contacts.put(toInt(rec.get("contact_id")), contact); + contacts.put(rec.getInteger("contact_id"), contact); - contact.setLabel(toLabel(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name"), rec.get("firma"))); - contact.setEmailAddresses(rec.get("email")); + contact.setLabel(toLabel(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"), rec.getString("firma"))); + contact.setEmailAddresses(rec.getString("email")); contact.setPostalAddress(toAddress(rec)); contact.setPhoneNumbers(toPhoneNumbers(rec)); } @@ -382,28 +431,28 @@ public class ImportOfficeTables extends ContextBasedTest { } private String toPhoneNumbers(final Record rec) { final var result = new StringBuilder("{\n"); - if (isNotBlank(rec.get("phone_private"))) - result.append(" \"private\": " + "\"" + rec.get("phone_private") + "\",\n"); - if (isNotBlank(rec.get("phone_office"))) - result.append(" \"office\": " + "\"" + rec.get("phone_office") + "\",\n"); - if (isNotBlank(rec.get("phone_mobile"))) - result.append(" \"mobile\": " + "\"" + rec.get("phone_mobile") + "\",\n"); - if (isNotBlank(rec.get("fax"))) - result.append(" \"fax\": " + "\"" + rec.get("fax") + "\",\n"); + if (isNotBlank(rec.getString("phone_private"))) + result.append(" \"private\": " + "\"" + rec.getString("phone_private") + "\",\n"); + if (isNotBlank(rec.getString("phone_office"))) + result.append(" \"office\": " + "\"" + rec.getString("phone_office") + "\",\n"); + if (isNotBlank(rec.getString("phone_mobile"))) + result.append(" \"mobile\": " + "\"" + rec.getString("phone_mobile") + "\",\n"); + if (isNotBlank(rec.getString("fax"))) + result.append(" \"fax\": " + "\"" + rec.getString("fax") + "\",\n"); return (result + "}").replace("\",\n}", "\"\n}"); } private String toAddress(final Record rec) { final var result = new StringBuilder(); - final var name = toName(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name")); + final var name = toName(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name")); if (isNotBlank(name)) result.append(name + "\n"); - if (isNotBlank(rec.get("firma"))) - result.append(rec.get("firma") + "\n"); - if (isNotBlank(rec.get("co"))) - result.append("c/o " + rec.get("co") + "\n"); - if (isNotBlank(rec.get("street"))) - result.append(rec.get("street") + "\n"); + if (isNotBlank(rec.getString("firma"))) + result.append(rec.getString("firma") + "\n"); + if (isNotBlank(rec.getString("co"))) + result.append("c/o " + rec.getString("co") + "\n"); + if (isNotBlank(rec.getString("street"))) + result.append(rec.getString("street") + "\n"); final var zipcodeAndCity = toZipcodeAndCity(rec); if (isNotBlank(zipcodeAndCity)) result.append(zipcodeAndCity + "\n"); @@ -412,12 +461,12 @@ public class ImportOfficeTables extends ContextBasedTest { private String toZipcodeAndCity(final Record rec) { final var result = new StringBuilder(); - if (isNotBlank(rec.get("country"))) - result.append(rec.get("country") + " "); - if (isNotBlank(rec.get("zipcode"))) - result.append(rec.get("zipcode") + " "); - if (isNotBlank(rec.get("city"))) - result.append(rec.get("city")); + if (isNotBlank(rec.getString("country"))) + result.append(rec.getString("country") + " "); + if (isNotBlank(rec.getString("zipcode"))) + result.append(rec.getString("zipcode") + " "); + if (isNotBlank(rec.getString("city"))) + result.append(rec.getString("city")); return result.toString(); } @@ -446,23 +495,6 @@ public class ImportOfficeTables extends ContextBasedTest { return toLabel(salut, title, firstname, lastname, null); } - private LocalDate toLocalDate(final String dateString) { - if (isNotBlank(dateString)) { - return LocalDate.parse(dateString); - } - return null; - } - - private static Integer toInt(final String value) { - return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; - } - - private LocalDate localDate(final String dateStringNullOrBlank) { - if (isNotBlank(dateStringNullOrBlank)) { - return LocalDate.parse(dateStringNullOrBlank); - } - return null; - } private Reader resourceReader(@NotNull final String resourcePath) { return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath)); @@ -512,7 +544,27 @@ class Record { this.row = row; } - String get(final String columnName) { + String getString(final String columnName) { return row[columns.indexOf(columnName)]; } + + Integer getInteger(final String columnName) { + return toInt(getString(columnName)); + } + + LocalDate getLocalDate(final String columnName) { + return toLocalDate(getString(columnName)); + } + + private static Integer toInt(final String value) { + return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; + } + + private LocalDate toLocalDate(final String dateString) { + if (isNotBlank(dateString)) { + return LocalDate.parse(dateString); + } + return null; + } + } -- 2.39.2 From 53d46da49ace778249320162bbea23b88081e3e4 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jan 2024 13:13:24 +0100 Subject: [PATCH 20/72] import coop assets --- .../HsOfficeCoopAssetsTransactionType.java | 9 +- .../HsOfficeCoopSharesTransactionType.java | 4 +- .../office/migration/ImportOfficeTables.java | 109 ++++++++++++++---- .../migration/asset-transactions.csv | 16 ++- 4 files changed, 107 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java index cc386966..2245f864 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java @@ -1,5 +1,12 @@ package net.hostsharing.hsadminng.hs.office.coopassets; public enum HsOfficeCoopAssetsTransactionType { - ADJUSTMENT, DEPOSIT, DISBURSAL, TRANSFER, ADOPTION, CLEARING, LOSS + ADJUSTMENT, // correction of wrong bookings + DEPOSIT, // payment received from member after signing shares, >0 + DISBURSAL, // payment send to member after cancellation of shares, <0 + TRANSFER, // transferring shares to another member, <0 + ADOPTION, // receiving shares from another member, >0 + CLEARING, // settlement with members dept, <0 + LOSS, // assignment of balance sheet loss in case of cancellation of shares, <0 + LIMITATION // limitation period was reached after impossible disbursal, <0 } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java index fedccc5c..e18451f5 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java @@ -1,5 +1,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; public enum HsOfficeCoopSharesTransactionType { - ADJUSTMENT, SUBSCRIPTION, CANCELLATION; + ADJUSTMENT, // correction of wrong bookings + SUBSCRIPTION, // shares signed, e.g. with the declaration of accession, >0 + CANCELLATION; // shares terminated, e.g. when a membership is resigned, <0 } 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 3db38f49..1e13bc6a 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 @@ -8,6 +8,7 @@ import net.hostsharing.hsadminng.context.ContextBasedTest; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; +import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType; import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionEntity; import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionType; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; @@ -38,12 +39,11 @@ import jakarta.validation.constraints.NotNull; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static java.util.Arrays.stream; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; @@ -65,15 +65,15 @@ import static org.assertj.core.api.Assertions.assertThat; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { - private static Map contacts = new HashMap<>(); - private static Map persons = new HashMap<>(); - private static Map partners = new HashMap<>(); - private static Map debitors = new HashMap<>(); - private static Map memberships = new HashMap<>(); - private static Map sepaMandates = new HashMap<>(); - private static Map bankAccounts = new HashMap<>(); - private static Map coopShares = new HashMap<>(); - private static Map assetTransactions = new HashMap<>(); + private static NavigableMap contacts = new TreeMap<>(); + private static NavigableMap persons = new TreeMap<>(); + private static NavigableMap partners = new TreeMap<>(); + private static NavigableMap debitors = new TreeMap<>(); + private static NavigableMap memberships = new TreeMap<>(); + private static NavigableMap sepaMandates = new TreeMap<>(); + private static NavigableMap bankAccounts = new TreeMap<>(); + private static NavigableMap coopShares = new TreeMap<>(); + private static NavigableMap coopAssets = new TreeMap<>(); @PersistenceContext EntityManager em; @@ -142,9 +142,9 @@ public class ImportOfficeTables extends ContextBasedTest { """); 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@example.org'), 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@example.org'), - 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com') } """); @@ -209,10 +209,37 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(coopShares.toString()).isEqualToIgnoringWhitespace(""" { - 33810=CoopShareTransaction(10010, 2016-12-31, CANCELLATION, 22, membership ended), 33443=CoopShareTransaction(10007, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), + 33451=CoopShareTransaction(10010, 2000-12-06, SUBSCRIPTION, 2, initial share subscription), 33701=CoopShareTransaction(10007, 2005-01-10, SUBSCRIPTION, 40, increase), - 33451=CoopShareTransaction(10010, 2000-12-06, SUBSCRIPTION, 2, initial share subscription) + 33810=CoopShareTransaction(10010, 2016-12-31, CANCELLATION, 22, membership ended) + } + """); + } + + @Test + @Order(5) + void importCoopAssets() { + + try (Reader reader = resourceReader("migration/asset-transactions.csv")) { + final var lines = readAllLines(reader); + importCoopAssets(justHeader(lines), withoutHeader(lines)); + } catch (Exception e) { + throw new RuntimeException(e); + } + + assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace(""" + { + 30000=CoopAssetsTransaction(10007, 2000-12-06, DEPOSIT, 1280.00, for subscription A), + 31001=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 18.00, for subscription B), + 31002=CoopAssetsTransaction(10010, 2000-12-06, -18.00, for subscription B), + 31003=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 128.00, for subscription B), + 32000=CoopAssetsTransaction(10007, 2005-01-10, DEPOSIT, 2560.00, for subscription C), + 33001=CoopAssetsTransaction(10007, 2005-01-10, -512.00, for transfer to 10), + 33002=CoopAssetsTransaction(10010, 2005-01-10, ADOPTION, 512.00, for transfer from 7), + 34001=CoopAssetsTransaction(10010, 2016-12-31, CLEARING, -8.00, for cancellation D), + 34002=CoopAssetsTransaction(10010, 2016-12-31, DISBURSAL, -100.00, for cancellation D), + 34003=CoopAssetsTransaction(10010, 2016-12-31, LOSS, -20.00, for cancellation D) } """); } @@ -355,6 +382,38 @@ public class ImportOfficeTables extends ContextBasedTest { }); } + private void importCoopAssets(final String[] header, final List records) { + + final var columns = new Columns(header); + + records.stream() + .map(this::trimAll) + .map(row -> new Record(columns, row)) + .forEach(rec -> { + final var member = memberships.get(rec.getInteger("bp_id")); + + final var assetTypeMapping = new HashMap() {{ + put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); + put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); + put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS); + put("CLEARING", HsOfficeCoopAssetsTransactionType.CLEARING); + put("PRESCRIPTION", HsOfficeCoopAssetsTransactionType.LIMITATION); + put("PAYBACK", HsOfficeCoopAssetsTransactionType.DISBURSAL); + put("PAYMENT", HsOfficeCoopAssetsTransactionType.DEPOSIT); + }}; + + final var assetTransaction = HsOfficeCoopAssetsTransactionEntity.builder() + .membership(member) + .valueDate(rec.getLocalDate("date")) + .transactionType(assetTypeMapping.get(rec.getString("action"))) + .assetValue(rec.getBigDecimal("amount")) + .reference(rec.getString("comment")) + .build(); + + coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction); + }); + } + private void importSepaMandates(final String[] header, final List records) { final var columns = new Columns(header); @@ -549,18 +608,20 @@ class Record { } Integer getInteger(final String columnName) { - return toInt(getString(columnName)); - } - - LocalDate getLocalDate(final String columnName) { - return toLocalDate(getString(columnName)); - } - - private static Integer toInt(final String value) { + final String value = getString(columnName); return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; } - private LocalDate toLocalDate(final String dateString) { + BigDecimal getBigDecimal(final String columnName) { + final String value = getString(columnName); + if (isNotBlank(value)) { + return new BigDecimal(value); + } + return null; + } + + LocalDate getLocalDate(final String columnName) { + final String dateString = getString(columnName); if (isNotBlank(dateString)) { return LocalDate.parse(dateString); } diff --git a/src/test/resources/migration/asset-transactions.csv b/src/test/resources/migration/asset-transactions.csv index 5d284e9e..5b4e10e7 100644 --- a/src/test/resources/migration/asset-transactions.csv +++ b/src/test/resources/migration/asset-transactions.csv @@ -1,5 +1,11 @@ -member_asset_id; bp_id; date; action; amount -33443; 7; 2000-12-06; PAYMENT; 1280 -33451; 10; 2000-12-06; PAYMENT; 128 -33701; 7; 2005-01-10; PAYMENT; 2560 -33810; 10; 2016-12-31; PAYBACK; 128 +member_asset_id; bp_id; date; action; amount; comment +30000; 7; 2000-12-06; PAYMENT; 1280.00; for subscription A +31001; 10; 2000-12-06; PAYMENT; 18.00; for subscription B +31002; 10; 2000-12-06; ADJUSTMENT; -18.00; for subscription B +31003; 10; 2000-12-06; PAYMENT; 128.00; for subscription B +32000; 7; 2005-01-10; PAYMENT; 2560.00; for subscription C +33001; 7; 2005-01-10; TRANSFER; -512.00; for transfer to 10 +33002; 10; 2005-01-10; ADOPTION; 512.00; for transfer from 7 +34001; 10; 2016-12-31; CLEARING; -8.00; for cancellation D +34002; 10; 2016-12-31; PAYBACK; -100.00; for cancellation D +34003; 10; 2016-12-31; LOSS; -20.00; for cancellation D -- 2.39.2 From 7e31e95d57ae5b2365ede763d30fee221aa2fd11 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 8 Jan 2024 13:48:31 +0100 Subject: [PATCH 21/72] persist shares+assets --- .../HsOfficeCoopAssetsTransactionEntity.java | 3 +- .../HsOfficeCoopSharesTransactionEntity.java | 3 +- .../office/migration/ImportOfficeTables.java | 40 ++++++++++++------- .../migration/asset-transactions.csv | 6 +-- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index e699fb5c..3342f052 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopassets; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; @@ -23,7 +24,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("CoopAssetsTransaction") -public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable { +public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class) .withProp(e -> e.getMembership().getMemberNumber()) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index b5d4979b..9f5aaf80 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; +import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; @@ -20,7 +21,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @NoArgsConstructor @AllArgsConstructor @DisplayName("CoopShareTransaction") -public class HsOfficeCoopSharesTransactionEntity implements Stringifyable { +public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeCoopSharesTransactionEntity.class) .withProp(e -> e.getMembership().getMemberNumber()) 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 1e13bc6a..dd1894cc 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 @@ -231,11 +231,9 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace(""" { 30000=CoopAssetsTransaction(10007, 2000-12-06, DEPOSIT, 1280.00, for subscription A), - 31001=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 18.00, for subscription B), - 31002=CoopAssetsTransaction(10010, 2000-12-06, -18.00, for subscription B), - 31003=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 128.00, for subscription B), + 31000=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 128.00, for subscription B), 32000=CoopAssetsTransaction(10007, 2005-01-10, DEPOSIT, 2560.00, for subscription C), - 33001=CoopAssetsTransaction(10007, 2005-01-10, -512.00, for transfer to 10), + 33001=CoopAssetsTransaction(10007, 2005-01-10, TRANSFER, -512.00, for transfer to 10), 33002=CoopAssetsTransaction(10010, 2005-01-10, ADOPTION, 512.00, for transfer from 7), 34001=CoopAssetsTransaction(10010, 2016-12-31, CLEARING, -8.00, for cancellation D), 34002=CoopAssetsTransaction(10010, 2016-12-31, DISBURSAL, -100.00, for cancellation D), @@ -267,7 +265,11 @@ public class ImportOfficeTables extends ContextBasedTest { sepaMandates.forEach((id, mandate) -> em.persist(mandate)); updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); - // TODO: coopshares+coopassets + coopShares.forEach((id, shareTransaction) -> em.persist(shareTransaction)); + updateLegacyIds(coopShares, "hs_office_coopsharestransaction_legacy_id", "member_share_id"); + + coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction)); + updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id"); em.flush(); } @@ -392,15 +394,25 @@ public class ImportOfficeTables extends ContextBasedTest { .forEach(rec -> { final var member = memberships.get(rec.getInteger("bp_id")); - final var assetTypeMapping = new HashMap() {{ - put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); - put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); - put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS); - put("CLEARING", HsOfficeCoopAssetsTransactionType.CLEARING); - put("PRESCRIPTION", HsOfficeCoopAssetsTransactionType.LIMITATION); - put("PAYBACK", HsOfficeCoopAssetsTransactionType.DISBURSAL); - put("PAYMENT", HsOfficeCoopAssetsTransactionType.DEPOSIT); - }}; + final var assetTypeMapping = new HashMap() { + { + put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); + put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); + put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS); + put("CLEARING", HsOfficeCoopAssetsTransactionType.CLEARING); + put("PRESCRIPTION", HsOfficeCoopAssetsTransactionType.LIMITATION); + put("PAYBACK", HsOfficeCoopAssetsTransactionType.DISBURSAL); + put("PAYMENT", HsOfficeCoopAssetsTransactionType.DEPOSIT); + } + + public HsOfficeCoopAssetsTransactionType get(final String key) { + final var value = super.get(key); + if ( value != null ) { + return value; + } + throw new IllegalStateException("no mapping value found for: " + key); + } + }; final var assetTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(member) diff --git a/src/test/resources/migration/asset-transactions.csv b/src/test/resources/migration/asset-transactions.csv index 5b4e10e7..1bde43fd 100644 --- a/src/test/resources/migration/asset-transactions.csv +++ b/src/test/resources/migration/asset-transactions.csv @@ -1,10 +1,8 @@ member_asset_id; bp_id; date; action; amount; comment 30000; 7; 2000-12-06; PAYMENT; 1280.00; for subscription A -31001; 10; 2000-12-06; PAYMENT; 18.00; for subscription B -31002; 10; 2000-12-06; ADJUSTMENT; -18.00; for subscription B -31003; 10; 2000-12-06; PAYMENT; 128.00; for subscription B +31000; 10; 2000-12-06; PAYMENT; 128.00; for subscription B 32000; 7; 2005-01-10; PAYMENT; 2560.00; for subscription C -33001; 7; 2005-01-10; TRANSFER; -512.00; for transfer to 10 +33001; 7; 2005-01-10; HANDOVER; -512.00; for transfer to 10 33002; 10; 2005-01-10; ADOPTION; 512.00; for transfer from 7 34001; 10; 2016-12-31; CLEARING; -8.00; for cancellation D 34002; 10; 2016-12-31; PAYBACK; -100.00; for cancellation D -- 2.39.2 From 1f278819c30be7e4a8cb3bef4650c4eab16f4f5f Mon Sep 17 00:00:00 2001 From: Michael Hierweck Date: Tue, 9 Jan 2024 12:28:57 +0100 Subject: [PATCH 22/72] HSDB Dump Script Script that is a able to dump HSDB (legacy) data to csv files. Dump of "office" module data already implemented. --- src/test/resources/migration/dump.sh | 41 ++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/test/resources/migration/dump.sh diff --git a/src/test/resources/migration/dump.sh b/src/test/resources/migration/dump.sh new file mode 100644 index 00000000..47335101 --- /dev/null +++ b/src/test/resources/migration/dump.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +host="127.0.0.1" +port="5432" +dbname="hsh02_hsdb" +username="hsh02_hsdb_readonly" + +target="/tmp" + +dump() { + sql="copy ($1) to stdout with csv header delimiter ';' quote '\"'" + file="${target}/${2}" + psql --host ${host} --port ${port} --user ${username} --command "${sql}" ${dbname} >"${file}" +} + +dump "select bp_id, member_id, member_code, member_since, member_until, member_role, author_contract, nondisc_contract, free, exempt_vat, indicator_vat, uid_vat + from business_partner + order by bp_id" \ + "business-partners.csv" + +dump "select contact_id, bp_id, salut, first_name, last_name, title, firma, co, street, zipcode, city, country, phone_private, phone_office, phone_mobile, fax, email, array_to_string(array_agg(role), ',') as roles + from contact + left join contactrole_ref using(contact_id) + group by contact_id + order by contact_id" \ + "contacts.csv" + +dump "select sepa_mandat_id, bp_id, bank_customer, bank_name, bank_iban, bank_bic, mandat_ref, mandat_signed, mandat_since, mandat_until, mandat_used + from sepa_mandat + order by sepa_mandat_id" \ + "sepa-mandates.csv" + +dump "select member_asset_id, bp_id, date, action, amount, comment + from member_asset + order by member_asset_id" \ + "asset-transactions.csv" + +dump "select member_share_id, bp_id, date, action, quantity, comment + from member_share + order by member_share_id" \ + "share-transactions.csv" -- 2.39.2 From 980524e7ab0f1d8300afadd3a826eaaf07638e8e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 9 Jan 2024 12:11:56 +0100 Subject: [PATCH 23/72] add: membership-fee-billable, debitor.billable, debitor.billingContact, debitor.defaultPrefix --- .../office/contact/HsOfficeContactEntity.java | 2 +- .../HsOfficeCoopAssetsTransactionEntity.java | 2 +- .../HsOfficeCoopSharesTransactionEntity.java | 2 +- .../office/debitor/HsOfficeDebitorEntity.java | 20 ++- .../debitor/HsOfficeDebitorEntityPatcher.java | 4 + .../debitor/HsOfficeDebitorRepository.java | 9 +- .../membership/HsOfficeMembershipEntity.java | 3 + .../office/migration}/HasUuid.java | 2 +- .../office/partner/HsOfficePartnerEntity.java | 10 +- .../HsOfficeSepaMandateEntity.java | 2 +- .../hs-office/hs-office-debitor-schemas.yaml | 29 +++- .../resources/db/changelog/010-context.sql | 2 +- .../db/changelog/054-rbac-context.sql | 4 +- .../db/changelog/220-hs-office-partner.sql | 1 + .../changelog/223-hs-office-partner-rbac.sql | 3 +- .../228-hs-office-partner-test-data.sql | 19 +-- .../db/changelog/270-hs-office-debitor.sql | 9 +- .../changelog/273-hs-office-debitor-rbac.sql | 13 +- .../278-hs-office-debitor-test-data.sql | 22 +-- .../db/changelog/300-hs-office-membership.sql | 3 +- .../303-hs-office-membership-rbac.sql | 8 +- .../308-hs-office-membership-test-data.sql | 30 ++-- .../hsadminng/arch/ArchitectureTest.java | 4 +- ...sTransactionRepositoryIntegrationTest.java | 5 +- ...sTransactionRepositoryIntegrationTest.java | 5 +- ...OfficeDebitorControllerAcceptanceTest.java | 55 ++++--- .../HsOfficeDebitorEntityPatcherUnitTest.java | 8 ++ .../HsOfficeDebitorEntityUnitTest.java | 32 ++++- ...fficeDebitorRepositoryIntegrationTest.java | 134 +++++++++++------- .../office/debitor/TestHsOfficeDebitor.java | 4 +- ...iceMembershipControllerAcceptanceTest.java | 22 +-- .../HsOfficeMembershipEntityUnitTest.java | 3 +- ...ceMembershipRepositoryIntegrationTest.java | 72 +++++----- .../office/migration/ImportOfficeTables.java | 34 +++-- ...fficePartnerRepositoryIntegrationTest.java | 69 ++++----- .../office/partner/TestHsOfficePartner.java | 5 +- ...ceSepaMandateControllerAcceptanceTest.java | 14 +- ...eSepaMandateRepositoryIntegrationTest.java | 24 ++-- .../resources/migration/business-partners.csv | 6 +- 39 files changed, 435 insertions(+), 260 deletions(-) rename src/main/java/net/hostsharing/hsadminng/{repository => hs/office/migration}/HasUuid.java (57%) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java index ccfbaf5f..012379b9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact; import lombok.*; import lombok.experimental.FieldNameConstants; import net.hostsharing.hsadminng.errors.DisplayName; -import net.hostsharing.hsadminng.repository.HasUuid; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 3342f052..ffc60bd1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopassets; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; -import net.hostsharing.hsadminng.repository.HasUuid; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 9f5aaf80..a9fd4bde 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; -import net.hostsharing.hsadminng.repository.HasUuid; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; 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 4a29be90..6520935d 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 @@ -40,13 +40,16 @@ public class HsOfficeDebitorEntity implements Stringifyable { @JoinColumn(name = "partneruuid") private HsOfficePartnerEntity partner; - @Column(name = "debitornumber") - private Integer debitorNumber; + @Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)") + private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String? @ManyToOne @JoinColumn(name = "billingcontactuuid") private HsOfficeContactEntity billingContact; + @Column(name = "billable") + private boolean billable; + @Column(name = "vatid") private String vatId; @@ -60,6 +63,17 @@ public class HsOfficeDebitorEntity implements Stringifyable { @JoinColumn(name = "refundbankaccountuuid") private HsOfficeBankAccountEntity refundBankAccount; + public String getDebitorNumberString() { + return partner.getDebitorNumberPrefix() + String.format("%02d", debitorNumberSuffix); + } + + public int getDebitorNumber() { + return Integer.parseInt(getDebitorNumberString()); + } + + @Column(name = "defaultprefix", columnDefinition = "char(3) not null") + private String defaultPrefix; + @Override public String toString() { return stringify.apply(this); @@ -67,6 +81,6 @@ public class HsOfficeDebitorEntity implements Stringifyable { @Override public String toShortString() { - return debitorNumber.toString(); + return getDebitorNumberString(); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java index b57b1ab2..2565ae30 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java @@ -31,6 +31,10 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher { + verifyNotNull(newValue, "defaultPrefix"); + entity.setDefaultPrefix(newValue); + }); } private void verifyNotNull(final Object newValue, final String propertyName) { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java index f0013ef9..5ca04719 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java @@ -13,9 +13,14 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByDebitorNumber(int debitorNumber); + List findDebitorByDebitorNumber(int debitorNumberPrefix, byte debitorNumberSuffix); + + default List findDebitorByDebitorNumber(int debitorNumber) { + return findDebitorByDebitorNumber( debitorNumber/100, (byte) (debitorNumber%100)); + } @Query(""" SELECT debitor FROM HsOfficeDebitorEntity debitor 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 671ae7f7..b637d3a7 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 @@ -58,6 +58,9 @@ public class HsOfficeMembershipEntity implements Stringifyable { @Type(PostgreSQLRangeType.class) private Range validity; + @Column(name = "membership_fee_billable") + private boolean membershipFeeBillable; + @Column(name = "reasonfortermination") @Enumerated(EnumType.STRING) private HsOfficeReasonForTermination reasonForTermination; diff --git a/src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java b/src/main/java/net/hostsharing/hsadminng/hs/office/migration/HasUuid.java similarity index 57% rename from src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java rename to src/main/java/net/hostsharing/hsadminng/hs/office/migration/HasUuid.java index 69db49cb..97e3eff1 100644 --- a/src/main/java/net/hostsharing/hsadminng/repository/HasUuid.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/migration/HasUuid.java @@ -1,4 +1,4 @@ -package net.hostsharing.hsadminng.repository; +package net.hostsharing.hsadminng.hs.office.migration; import java.util.UUID; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java index 2dab9e55..95a30290 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java @@ -3,14 +3,15 @@ package net.hostsharing.hsadminng.hs.office.partner; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; 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.repository.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFoundAction; import jakarta.persistence.*; +import java.util.Optional; import java.util.UUID; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @@ -35,6 +36,9 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { @GeneratedValue private UUID uuid; + @Column(name = "debitornumberprefix", columnDefinition = "numeric(5) not null") + private Integer debitorNumberPrefix; + @ManyToOne @JoinColumn(name = "personuuid", nullable = false) private HsOfficePersonEntity person; @@ -44,7 +48,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { private HsOfficeContactEntity contact; @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true) - @JoinColumn(name = "detailsuuid", nullable = true) + @JoinColumn(name = "detailsuuid") @NotFound(action = NotFoundAction.IGNORE) private HsOfficePartnerDetailsEntity details; @@ -55,6 +59,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { @Override public String toShortString() { - return person.toShortString(); + return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse(""); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java index aefc34c4..bdd0b045 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateEntity.java @@ -6,7 +6,7 @@ import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; -import net.hostsharing.hsadminng.repository.HasUuid; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.Type; diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml index 583ae8fa..372b57bf 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml @@ -12,8 +12,13 @@ components: debitorNumber: type: integer format: int32 - minimum: 10000 - maximum: 99999 + minimum: 1000000 + maximum: 9999999 + debitorNumberSuffix: + type: integer + format: int8 + minimum: 00 + maximum: 99 partner: $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' billingContact: @@ -27,6 +32,9 @@ components: type: boolean refundBankAccount: $ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount' + defaultPrefix: + type: string + pattern: '^[a-z0-9]{3}$' HsOfficeDebitorPatch: type: object @@ -49,6 +57,10 @@ components: type: string format: uuid nullable: true + defaultPrefix: + type: string + pattern: '^[a-z0-9]{3}$' + nullable: true HsOfficeDebitorInsert: type: object @@ -61,11 +73,11 @@ components: type: string format: uuid nullable: false - debitorNumber: + debitorNumberSuffix: type: integer - format: int32 - minimum: 10000 - maximum: 99999 + format: int8 + minimum: 00 + maximum: 99 vatId: type: string vatCountryCode: @@ -76,6 +88,11 @@ components: refundBankAccountUuid: type: string format: uuid + defaultPrefix: + type: string + pattern: '^[a-z]{3}$' + required: - partnerUuid - billingContactUuid + - defaultPrefix diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql index 9d640165..71559b7b 100644 --- a/src/main/resources/db/changelog/010-context.sql +++ b/src/main/resources/db/changelog/010-context.sql @@ -155,7 +155,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar) declare cleanIdentifier varchar; begin - cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._]+', '', 'g'); + cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g'); return cleanIdentifier; end; $$; diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/054-rbac-context.sql index f1c8cfdc..32a652a6 100644 --- a/src/main/resources/db/changelog/054-rbac-context.sql +++ b/src/main/resources/db/changelog/054-rbac-context.sql @@ -28,8 +28,8 @@ create or replace function determineCurrentSubjectsUuids(currentUserUuid uuid, a stable leakproof language plpgsql as $$ declare - roleName varchar(63); - roleNameParts varchar(63); + roleName text; + roleNameParts text; objectTableToAssume varchar(63); objectNameToAssume varchar(63); objectUuidToAssume uuid; diff --git a/src/main/resources/db/changelog/220-hs-office-partner.sql b/src/main/resources/db/changelog/220-hs-office-partner.sql index dc01228d..689bb1f0 100644 --- a/src/main/resources/db/changelog/220-hs-office-partner.sql +++ b/src/main/resources/db/changelog/220-hs-office-partner.sql @@ -31,6 +31,7 @@ call create_journal('hs_office_partner_details'); create table hs_office_partner ( uuid uuid unique references RbacObject (uuid) initially deferred, + debitorNumberPrefix varchar(5), personUuid uuid not null references hs_office_person(uuid), contactUuid uuid not null references hs_office_contact(uuid), detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade diff --git a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql index 83a99573..32f629dc 100644 --- a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql @@ -27,7 +27,6 @@ create or replace function hsOfficePartnerRbacRolesTrigger() language plpgsql strict as $$ declare - hsOfficePartnerTenant RbacRoleDescriptor; oldPerson hs_office_person; newPerson hs_office_person; oldContact hs_office_contact; @@ -166,6 +165,8 @@ execute procedure hsOfficePartnerRbacRolesTrigger(); --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_partner', $idName$ + -- TODO: simplify by using just debitorNumberPrefix for the essential part + debitorNumberPrefix || ':' || (select idName from hs_office_person_iv p where p.uuid = target.personuuid) || '-' || (select idName from hs_office_contact_iv c where c.uuid = target.contactuuid) diff --git a/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql index 648a850d..759c48c9 100644 --- a/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql +++ b/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql @@ -8,7 +8,10 @@ /* Creates a single partner test record. */ -create or replace procedure createHsOfficePartnerTestData( personTradeOrFamilyName varchar, contactLabel varchar ) +create or replace procedure createHsOfficePartnerTestData( + debitorNumberPrefix numeric(5), + personTradeOrFamilyName varchar, + contactLabel varchar ) language plpgsql as $$ declare currentTask varchar; @@ -51,8 +54,8 @@ begin end if; insert - into hs_office_partner (uuid, personuuid, contactuuid, detailsUuid) - values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); + into hs_office_partner (uuid, debitorNumberPrefix, personuuid, contactuuid, detailsUuid) + values (uuid_generate_v4(), debitorNumberPrefix, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); end; $$; --// @@ -64,11 +67,11 @@ end; $$; do language plpgsql $$ begin - call createHsOfficePartnerTestData('First GmbH', 'first contact'); - call createHsOfficePartnerTestData('Second e.K.', 'second contact'); - call createHsOfficePartnerTestData('Third OHG', 'third contact'); - call createHsOfficePartnerTestData('Fourth e.G.', 'forth contact'); - call createHsOfficePartnerTestData('Smith', 'fifth contact'); + call createHsOfficePartnerTestData(10001, 'First GmbH', 'first contact'); + call createHsOfficePartnerTestData(10002, 'Second e.K.', 'second contact'); + call createHsOfficePartnerTestData(10003, 'Third OHG', 'third contact'); + call createHsOfficePartnerTestData(10004, 'Fourth e.G.', 'forth contact'); + call createHsOfficePartnerTestData(10010, 'Smith', 'fifth contact'); end; $$; --// diff --git a/src/main/resources/db/changelog/270-hs-office-debitor.sql b/src/main/resources/db/changelog/270-hs-office-debitor.sql index 10da431e..a860ab80 100644 --- a/src/main/resources/db/changelog/270-hs-office-debitor.sql +++ b/src/main/resources/db/changelog/270-hs-office-debitor.sql @@ -8,12 +8,17 @@ create table hs_office_debitor ( uuid uuid unique references RbacObject (uuid) initially deferred, partnerUuid uuid not null references hs_office_partner(uuid), - debitorNumber numeric(5) not null, + billable boolean not null default true, + debitorNumberSuffix numeric(2) not null, billingContactUuid uuid not null references hs_office_contact(uuid), vatId varchar(24), -- TODO.spec: here or in person? vatCountryCode varchar(2), vatBusiness boolean not null, -- TODO.spec: more of such? - refundBankAccountUuid uuid references hs_office_bankaccount(uuid) + refundBankAccountUuid uuid references hs_office_bankaccount(uuid), + defaultPrefix char(3) not null + constraint check_member_code check ( + defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$' + ) -- TODO.impl: SEPA-mandate ); --// diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index 01b740c7..172ae33a 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -172,8 +172,10 @@ execute procedure hsOfficeDebitorRbacRolesTrigger(); --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_debitor', $idName$ - '#' || debitorNumber || ':' || - (select idName from hs_office_partner_iv p where p.uuid = target.partnerUuid) + '#' || + (select debitornumberprefix from hs_office_partner p where p.uuid = target.partnerUuid) || + to_char(debitorNumberSuffix, 'fm00') || + ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid) $idName$); --// @@ -181,14 +183,17 @@ call generateRbacIdentityView('hs_office_debitor', $idName$ -- ============================================================================ --changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumber', +call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix', $updates$ partnerUuid = new.partnerUuid, + billable = new.billable, billingContactUuid = new.billingContactUuid, + debitorNumberSuffix = new.debitorNumberSuffix, refundBankAccountUuid = new.refundBankAccountUuid, vatId = new.vatId, vatCountryCode = new.vatCountryCode, - vatBusiness = new.vatBusiness + vatBusiness = new.vatBusiness, + defaultPrefix = new.defaultPrefix $updates$); --// diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql index 1435a86d..3b7fb81e 100644 --- a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql +++ b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql @@ -8,7 +8,12 @@ /* Creates a single debitor test record. */ -create or replace procedure createHsOfficeDebitorTestData( partnerTradeName varchar, billingContactLabel varchar ) +create or replace procedure createHsOfficeDebitorTestData( + debitorNumberSuffix numeric(5), + partnerTradeName varchar, + billingContactLabel varchar, + defaultPrefix varchar + ) language plpgsql as $$ declare currentTask varchar; @@ -16,7 +21,6 @@ declare relatedPartner hs_office_partner; relatedContact hs_office_contact; relatedBankAccountUuid uuid; - newDebitorNumber numeric(6); begin idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel); currentTask := 'creating debitor test-data ' || idName; @@ -28,14 +32,14 @@ begin where person.tradeName = partnerTradeName into relatedPartner; select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact; select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid; - select coalesce(max(debitorNumber)+1, 10001) from hs_office_debitor into newDebitorNumber; - raise notice 'creating test debitor: % (#%)', idName, newDebitorNumber; + -- raise notice 'creating test debitor: % (#%)', idName, relatedPartner.debitorNumberPrefix || '00'; + raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact; insert - into hs_office_debitor (uuid, partneruuid, debitornumber, billingcontactuuid, vatbusiness, refundbankaccountuuid) - values (uuid_generate_v4(), relatedPartner.uuid, newDebitorNumber, relatedContact.uuid, true, relatedBankAccountUuid); + into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, refundbankaccountuuid, defaultprefix) + values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, relatedBankAccountUuid, defaultPrefix); end; $$; --// @@ -46,9 +50,9 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeDebitorTestData('First GmbH', 'first contact'); - call createHsOfficeDebitorTestData('Second e.K.', 'second contact'); - call createHsOfficeDebitorTestData('Third OHG', 'third contact'); + call createHsOfficeDebitorTestData(11, 'First GmbH', 'first contact', 'fir'); + call createHsOfficeDebitorTestData(12, 'Second e.K.', 'second contact', 'sec'); + call createHsOfficeDebitorTestData(13, 'Third OHG', 'third contact', 'thi'); end; $$; --// diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index 09fe2e44..69f21799 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -15,7 +15,8 @@ create table if not exists hs_office_membership mainDebitorUuid uuid not null references hs_office_debitor(uuid), memberNumber numeric(5) not null unique, validity daterange not null, - reasonForTermination HsOfficeReasonForTermination not null default 'NONE' + reasonForTermination HsOfficeReasonForTermination not null default 'NONE', + membership_fee_billable boolean not null default true ); --// diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index 4335c32d..5f749445 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -27,7 +27,7 @@ create or replace function hsOfficeMembershipRbacRolesTrigger() language plpgsql strict as $$ declare - newHsOfficePartner hs_office_partner; + newHsOfficePartner hs_office_partner; newHsOfficeDebitor hs_office_debitor; begin @@ -92,7 +92,8 @@ execute procedure hsOfficeMembershipRbacRolesTrigger(); --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$ - target.memberNumber || (select idName from hs_office_partner_iv p where p.uuid = target.partnerUuid) + target.memberNumber || + ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid) $idName$); --// @@ -104,7 +105,8 @@ call generateRbacRestrictedView('hs_office_membership', orderby => 'target.memberNumber', columnUpdates => $updates$ validity = new.validity, - reasonForTermination = new.reasonForTermination + reasonForTermination = new.reasonForTermination, + membership_fee_billable = new.membership_fee_billable $updates$); --// diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql index 5b229466..1684ff09 100644 --- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql +++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql @@ -8,14 +8,17 @@ /* Creates a single membership test record. */ +-- create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber integer ) create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric ) language plpgsql as $$ declare - currentTask varchar; - idName varchar; - relatedPartner hs_office_partner; - relatedDebitor hs_office_debitor; - newMemberNumber numeric; + currentTask varchar; + idName varchar; +-- forDebitorNumberPrefix integer; +-- forDebitorNumberSuffix integer; + relatedPartner hs_office_partner; + relatedDebitor hs_office_debitor; + newMemberNumber numeric; begin idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumber); currentTask := 'creating Membership test-data ' || idName; @@ -25,7 +28,10 @@ begin select partner.* from hs_office_partner partner join hs_office_person person on person.uuid = partner.personUuid where person.tradeName = forPartnerTradeName into relatedPartner; - select d.* from hs_office_debitor d where d.debitorNumber = forMainDebitorNumber into relatedDebitor; +-- forDebitorNumberPrefix := forMainDebitorNumber/ 100; +-- forDebitorNumberSuffix := mod(forMainDebitorNumber, 100); +-- select d.* from hs_office_debitor d where d.debitorNumberSuffix = forDebitorNumberSuffix into relatedDebitor; + select d.* from hs_office_debitor d where d.debitorNumberSuffix = forMainDebitorNumber into relatedDebitor; select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber; raise notice 'creating test Membership: %', idName; @@ -33,6 +39,7 @@ begin raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; insert into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination) + -- values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, forDebitorNumberPrefix, daterange('20221001' , null, '[]'), 'NONE'); values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE'); end; $$; --// @@ -44,9 +51,14 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeMembershipTestData('First GmbH', 10001); - call createHsOfficeMembershipTestData('Second e.K.', 10002); - call createHsOfficeMembershipTestData('Third OHG', 10003); + call createHsOfficeMembershipTestData('First GmbH', 11); + call createHsOfficeMembershipTestData('Second e.K.', 12); + call createHsOfficeMembershipTestData('Third OHG', 13); end; +-- begin +-- call createHsOfficeMembershipTestData('First GmbH', 1000100); +-- call createHsOfficeMembershipTestData('Second e.K.', 1000200); +-- call createHsOfficeMembershipTestData('Third OHG', 1000300); +-- end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java index a5783201..a09e00b9 100644 --- a/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java +++ b/src/test/java/net/hostsharing/hsadminng/arch/ArchitectureTest.java @@ -200,7 +200,9 @@ public class ArchitectureTest { public static final ArchRule hsOfficeCoopSharesPackageRule = classes() .that().resideInAPackage("..hs.office.coopshares..") .should().onlyBeAccessed().byClassesThat() - .resideInAnyPackage("..hs.office.coopshares.."); + .resideInAnyPackage( + "..hs.office.coopshares..", + "..hs.office.migration.."); @ArchTest @SuppressWarnings("unused") diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index 9728e438..851ccb69 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -117,7 +117,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm view on coopassetstransaction#temprefB to role membership#10001....tenant by system and assume }", + "{ grant perm view on coopassetstransaction#temprefB to role membership#10001:....tenant by system and assume }", null)); } @@ -200,8 +200,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase @Test public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() { // given: - context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin"); - // "hs_office_person#FirstGmbH.admin", + context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin"); // when: final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index 772c59c2..8dee222f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -116,7 +116,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm view on coopsharestransaction#temprefB to role membership#10001....tenant by system and assume }", + "{ grant perm view on coopsharestransaction#temprefB to role membership#10001:....tenant by system and assume }", null)); } @@ -199,8 +199,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase @Test public void normalUser_canViewOnlyRelatedCoopSharesTransactions() { // given: - context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin"); - // "hs_office_person#FirstGmbH.admin", + context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin"); // when: final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index 76d6758f..cba8e203 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -35,8 +35,8 @@ import static org.hamcrest.Matchers.*; @Transactional class HsOfficeDebitorControllerAcceptanceTest { - private static final int LOWEST_TEMP_DEBITOR_NUMBER = 20000; - private static int nextDebitorNumber = LOWEST_TEMP_DEBITOR_NUMBER; + private static final int LOWEST_TEMP_DEBITOR_SUFFIX = 90; + private static byte nextDebitorSuffix = LOWEST_TEMP_DEBITOR_SUFFIX; @LocalServerPort private Integer port; @@ -81,7 +81,8 @@ class HsOfficeDebitorControllerAcceptanceTest { .body("", lenientlyEquals(""" [ { - "debitorNumber": 10001, + "debitorNumber": 1000111, + "debitorNumberSuffix": 11, "partner": { "person": { "personType": "LEGAL" } }, "billingContact": { "label": "first contact" }, "vatId": null, @@ -90,7 +91,8 @@ class HsOfficeDebitorControllerAcceptanceTest { "refundBankAccount": { "holder": "First GmbH" } }, { - "debitorNumber": 10002, + "debitorNumber": 1000212, + "debitorNumberSuffix": 12, "partner": { "person": { "tradeName": "Second e.K." } }, "billingContact": { "label": "second contact" }, "vatId": null, @@ -99,7 +101,8 @@ class HsOfficeDebitorControllerAcceptanceTest { "refundBankAccount": { "holder": "Second e.K." } }, { - "debitorNumber": 10003, + "debitorNumber": 1000313, + "debitorNumberSuffix": 13, "partner": { "person": { "tradeName": "Third OHG" } }, "billingContact": { "label": "third contact" }, "vatId": null, @@ -120,14 +123,14 @@ class HsOfficeDebitorControllerAcceptanceTest { .header("current-user", "superuser-alex@hostsharing.net") .port(port) .when() - .get("http://localhost/api/hs/office/debitors?debitorNumber=10002") + .get("http://localhost/api/hs/office/debitors?debitorNumber=1000212") .then().log().all().assertThat() .statusCode(200) .contentType("application/json") .body("", lenientlyEquals(""" [ { - "debitorNumber": 10002, + "debitorNumber": 1000212, "partner": { "person": { "tradeName": "Second e.K." } }, "billingContact": { "label": "second contact" }, "vatId": null, @@ -160,13 +163,14 @@ class HsOfficeDebitorControllerAcceptanceTest { { "partnerUuid": "%s", "billingContactUuid": "%s", - "debitorNumber": "%s", + "debitorNumberSuffix": "%s", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, - "refundBankAccountUuid": "%s" + "refundBankAccountUuid": "%s", + "defaultPrefix": "for" } - """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber, givenBankAccount.getUuid())) + """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix, givenBankAccount.getUuid())) .port(port) .when() .post("http://localhost/api/hs/office/debitors") @@ -175,6 +179,7 @@ class HsOfficeDebitorControllerAcceptanceTest { .contentType(ContentType.JSON) .body("uuid", isUuidValid()) .body("vatId", is("VAT123456")) + .body("defaultPrefix", is("for")) .body("billingContact.label", is(givenContact.getLabel())) .body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName())) .body("refundBankAccount.holder", is(givenBankAccount.getHolder())) @@ -202,9 +207,10 @@ class HsOfficeDebitorControllerAcceptanceTest { { "partnerUuid": "%s", "billingContactUuid": "%s", - "debitorNumber": "%s" + "debitorNumberSuffix": "%s", + "defaultPrefix": "for" } - """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber)) + """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix)) .port(port) .when() .post("http://localhost/api/hs/office/debitors") @@ -218,6 +224,7 @@ class HsOfficeDebitorControllerAcceptanceTest { .body("vatCountryCode", equalTo(null)) .body("vatBusiness", equalTo(false)) .body("refundBankAccount", equalTo(null)) + .body("defaultPrefix", equalTo("for")) .header("Location", startsWith("http://localhost")) .extract().header("Location"); // @formatter:on @@ -242,12 +249,14 @@ class HsOfficeDebitorControllerAcceptanceTest { { "partnerUuid": "%s", "billingContactUuid": "%s", - "debitorNumber": "%s", + "debitorNumberSuffix": "%s", "vatId": "VAT123456", "vatCountryCode": "DE", - "vatBusiness": true + "vatBusiness": true, + "defaultPrefix": "thi" } - """.formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorNumber)) + """ + .formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorSuffix)) .port(port) .when() .post("http://localhost/api/hs/office/debitors") @@ -272,12 +281,13 @@ class HsOfficeDebitorControllerAcceptanceTest { { "partnerUuid": "%s", "billingContactUuid": "%s", - "debitorNumber": "%s", + "debitorNumberSuffix": "%s", "vatId": "VAT123456", "vatCountryCode": "DE", - "vatBusiness": true + "vatBusiness": true, + "defaultPrefix": "for" } - """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorNumber)) + """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix)) .port(port) .when() .post("http://localhost/api/hs/office/debitors") @@ -375,7 +385,8 @@ class HsOfficeDebitorControllerAcceptanceTest { "contactUuid": "%s", "vatId": "VAT222222", "vatCountryCode": "AA", - "vatBusiness": true + "vatBusiness": true, + "defaultPrefix": "for" } """.formatted(givenContact.getUuid())) .port(port) @@ -388,6 +399,7 @@ class HsOfficeDebitorControllerAcceptanceTest { .body("vatId", is("VAT222222")) .body("vatCountryCode", is("AA")) .body("vatBusiness", is(true)) + .body("defaultPrefix", is("for")) .body("billingContact.label", is(givenContact.getLabel())) .body("partner.person.tradeName", is(givenDebitor.getPartner().getPerson().getTradeName())); // @formatter:on @@ -522,9 +534,10 @@ class HsOfficeDebitorControllerAcceptanceTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newDebitor = HsOfficeDebitorEntity.builder() - .debitorNumber(++nextDebitorNumber) + .debitorNumberSuffix(++nextDebitorSuffix) .partner(givenPartner) .billingContact(givenContact) + .defaultPrefix("abc") .build(); return debitorRepo.save(newDebitor); @@ -537,7 +550,7 @@ class HsOfficeDebitorControllerAcceptanceTest { jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net"); final var count = em.createQuery( - "DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumber > " + LOWEST_TEMP_DEBITOR_NUMBER) + "DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumberSuffix >= " + LOWEST_TEMP_DEBITOR_SUFFIX) .executeUpdate(); System.out.printf("deleted %d entities%n", count); }); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java index 6edb6cd8..8703ccdb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java @@ -31,6 +31,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID(); + private static final String PATCHED_DEFAULT_PREFIX = "xyz"; private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ"; private static final boolean PATCHED_VAT_BUSINESS = false; @@ -62,6 +63,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< entity.setVatId("initial VAT-ID"); entity.setVatCountryCode("AA"); entity.setVatBusiness(true); + entity.setDefaultPrefix("abc"); return entity; } @@ -100,6 +102,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorPatchResource::setVatBusiness, PATCHED_VAT_BUSINESS, HsOfficeDebitorEntity::setVatBusiness) + .notNullable(), + new JsonNullableProperty<>( + "defaultPrefix", + HsOfficeDebitorPatchResource::setDefaultPrefix, + PATCHED_DEFAULT_PREFIX, + HsOfficeDebitorEntity::setDefaultPrefix) .notNullable() ); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java index 0f147f93..31e93492 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java @@ -13,30 +13,50 @@ class HsOfficeDebitorEntityUnitTest { @Test void toStringContainsPartnerAndContact() { final var given = HsOfficeDebitorEntity.builder() - .debitorNumber(123456) + .debitorNumberSuffix((byte)67) .partner(HsOfficePartnerEntity.builder() .person(HsOfficePersonEntity.builder() .tradeName("some trade name") .build()) .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) - + .debitorNumberPrefix(12345) .build()) .billingContact(HsOfficeContactEntity.builder().label("some label").build()) .build(); final var result = given.toString(); - assertThat(result).isEqualTo("debitor(123456: some trade name)"); + assertThat(result).isEqualTo("debitor(1234567: some trade name)"); } @Test - void toShortStringContainsPartnerAndContact() { + void toStringWithoutPersonContainsDebitorNumber() { final var given = HsOfficeDebitorEntity.builder() - .debitorNumber(123456) + .debitorNumberSuffix((byte)67) + .partner(HsOfficePartnerEntity.builder() + .person(null) + .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) + .debitorNumberPrefix(12345) + .build()) + .billingContact(HsOfficeContactEntity.builder().label("some label").build()) + .build(); + + final var result = given.toString(); + + assertThat(result).isEqualTo("debitor(1234567: )"); + } + + @Test + void toShortStringContainsDebitorNumber() { + final var given = HsOfficeDebitorEntity.builder() + .partner(HsOfficePartnerEntity.builder() + .debitorNumberPrefix(12345) + .build()) + .debitorNumberSuffix((byte)67) .build(); final var result = given.toShortString(); - assertThat(result).isEqualTo("123456"); + assertThat(result).isEqualTo("1234567"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index b8ab0710..fd2e3a8d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -81,9 +81,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // when final var result = attempt(em, () -> { final var newDebitor = HsOfficeDebitorEntity.builder() - .debitorNumber(20001) + .debitorNumberSuffix((byte)21) .partner(givenPartner) .billingContact(givenContact) + .defaultPrefix("abc") .build(); return debitorRepo.save(newDebitor); }); @@ -95,14 +96,43 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { assertThat(debitorRepo.count()).isEqualTo(count + 1); } +// @ParameterizedTest +// @ValueSource(strings = {"", "a", "ab", "a12", "123", "12a"}) +// public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) { +// // given +// context("superuser-alex@hostsharing.net"); +// final var count = debitorRepo.count(); +// final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); +// final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); +// +// // when +// final var result = attempt(em, () -> { +// final var newDebitor = HsOfficeDebitorEntity.builder() +// .debitorNumberSuffix((byte)21) +// .partner(givenPartner) +// .billingContact(givenContact) +// .defaultPrefix(givenPrefix) +// .build(); +// return debitorRepo.save(newDebitor); +// }); +// +// // then +// result.assertSuccessful(); +// assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeDebitorEntity::getUuid).isNotNull(); +// assertThatDebitorIsPersisted(result.returnedValue()); +// assertThat(debitorRepo.count()).isEqualTo(count + 1); +// } + @Test public void createsAndGrantsRoles() { // given context("superuser-alex@hostsharing.net"); final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream() + // some search+replace to make the output fit into the screen width .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) - .map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG")) +// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) + .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("hs_office_", "")) @@ -113,9 +143,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newDebitor = HsOfficeDebitorEntity.builder() - .debitorNumber(20002) + .debitorNumberSuffix((byte)22) .partner(givenPartner) .billingContact(givenContact) + .defaultPrefix("abc") .build(); return debitorRepo.save(newDebitor); }).assertSuccessful(); @@ -123,42 +154,43 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_debitor#20002Fourthe.G.-forthcontact.owner", - "hs_office_debitor#20002Fourthe.G.-forthcontact.admin", - "hs_office_debitor#20002Fourthe.G.-forthcontact.agent", - "hs_office_debitor#20002Fourthe.G.-forthcontact.tenant", - "hs_office_debitor#20002Fourthe.G.-forthcontact.guest")); + "hs_office_debitor#1000422:Fourthe.G.-forthcontact.owner", + "hs_office_debitor#1000422:Fourthe.G.-forthcontact.admin", + "hs_office_debitor#1000422:Fourthe.G.-forthcontact.agent", + "hs_office_debitor#1000422:Fourthe.G.-forthcontact.tenant", + "hs_office_debitor#1000422:Fourthe.G.-forthcontact.guest")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) - .map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG")) +// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) + .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, // owner - "{ grant perm * on debitor#FeG to role debitor#FeG.owner by system and assume }", - "{ grant role debitor#FeG.owner to role global#global.admin by system and assume }", - "{ grant role debitor#FeG.owner to user superuser-alex by global#global.admin and assume }", + "{ grant perm * on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }", + "{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }", + "{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }", // admin - "{ grant perm edit on debitor#FeG to role debitor#FeG.admin by system and assume }", - "{ grant role debitor#FeG.admin to role debitor#FeG.owner by system and assume }", + "{ grant perm edit on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }", + "{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }", // agent - "{ grant role debitor#FeG.agent to role debitor#FeG.admin by system and assume }", - "{ grant role debitor#FeG.agent to role contact#4th.admin by system and assume }", - "{ grant role debitor#FeG.agent to role partner#FeG.admin by system and assume }", + "{ grant role debitor#1000422:FeG.agent to role debitor#1000422:FeG.admin by system and assume }", + "{ grant role debitor#1000422:FeG.agent to role contact#4th.admin by system and assume }", + "{ grant role debitor#1000422:FeG.agent to role partner#10004:FeG.admin by system and assume }", // tenant - "{ grant role contact#4th.guest to role debitor#FeG.tenant by system and assume }", - "{ grant role debitor#FeG.tenant to role debitor#FeG.agent by system and assume }", - "{ grant role debitor#FeG.tenant to role partner#FeG.agent by system and assume }", - "{ grant role partner#FeG.tenant to role debitor#FeG.tenant by system and assume }", + "{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }", + "{ grant role debitor#1000422:FeG.tenant to role debitor#1000422:FeG.agent by system and assume }", + "{ grant role debitor#1000422:FeG.tenant to role partner#10004:FeG.agent by system and assume }", + "{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }", // guest - "{ grant perm view on debitor#FeG to role debitor#FeG.guest by system and assume }", - "{ grant role debitor#FeG.guest to role debitor#FeG.tenant by system and assume }", + "{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }", + "{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }", null)); } @@ -183,14 +215,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then allTheseDebitorsAreReturned( result, - "debitor(10001: First GmbH)", - "debitor(10002: Second e.K.)", - "debitor(10003: Third OHG)"); + "debitor(1000111: First GmbH)", + "debitor(1000212: Second e.K.)", + "debitor(1000313: Third OHG)"); } @ParameterizedTest @ValueSource(strings = { - "hs_office_partner#FirstGmbH-firstcontact.admin", + "hs_office_partner#10001:FirstGmbH-firstcontact.admin", "hs_office_person#FirstGmbH.admin", "hs_office_contact#firstcontact.admin", }) @@ -202,7 +234,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike(null); // then: - exactlyTheseDebitorsAreReturned(result, "debitor(10001: First GmbH)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000111: First GmbH)", "debitor(1000120: First GmbH)"); } @Test @@ -227,10 +259,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = debitorRepo.findDebitorByDebitorNumber(10003); + final var result = debitorRepo.findDebitorByDebitorNumber(1000313); // then - exactlyTheseDebitorsAreReturned(result, "debitor(10003: Third OHG)"); +// exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)", "debitor(1000413: Fourth e.G.)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)"); } } @@ -246,7 +279,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); // then - exactlyTheseDebitorsAreReturned(result, "debitor(10003: Third OHG)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)"); } } @@ -257,10 +290,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void globalAdmin_canUpdateArbitraryDebitor() { // given context("superuser-alex@hostsharing.net"); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif"); assertThatDebitorIsVisibleForUserWithRole( givenDebitor, - "hs_office_partner#Fourthe.G.-forthcontact.admin"); + "hs_office_partner#10004:Fourthe.G.-forthcontact.admin"); assertThatDebitorActuallyInDatabase(givenDebitor); final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0); @@ -290,10 +323,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // ... partner role was reassigned: assertThatDebitorIsNotVisibleForUserWithRole( result.returnedValue(), - "hs_office_partner#Fourthe.G.-forthcontact.agent"); + "hs_office_partner#10004:Fourthe.G.-forthcontact.agent"); assertThatDebitorIsVisibleForUserWithRole( result.returnedValue(), - "hs_office_partner#FirstGmbH-firstcontact.agent"); + "hs_office_partner#10001:FirstGmbH-firstcontact.agent"); // ... contact role was reassigned: assertThatDebitorIsNotVisibleForUserWithRole( @@ -316,10 +349,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void globalAdmin_canUpdateNullRefundBankAccountToNotNullBankAccountForArbitraryDebitor() { // given context("superuser-alex@hostsharing.net"); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fif"); assertThatDebitorIsVisibleForUserWithRole( givenDebitor, - "hs_office_partner#Fourthe.G.-forthcontact.admin"); + "hs_office_partner#10004:Fourthe.G.-forthcontact.admin"); assertThatDebitorActuallyInDatabase(givenDebitor); final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0); @@ -346,10 +379,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void globalAdmin_canUpdateRefundBankAccountToNullForArbitraryDebitor() { // given context("superuser-alex@hostsharing.net"); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif"); assertThatDebitorIsVisibleForUserWithRole( givenDebitor, - "hs_office_partner#Fourthe.G.-forthcontact.admin"); + "hs_office_partner#10004:Fourthe.G.-forthcontact.admin"); assertThatDebitorActuallyInDatabase(givenDebitor); // when @@ -375,15 +408,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void partnerAdmin_canNotUpdateRelatedDebitor() { // given context("superuser-alex@hostsharing.net"); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig"); assertThatDebitorIsVisibleForUserWithRole( givenDebitor, - "hs_office_partner#Fourthe.G.-forthcontact.admin"); + "hs_office_partner#10004:Fourthe.G.-forthcontact.admin"); assertThatDebitorActuallyInDatabase(givenDebitor); // when final var result = jpaAttempt.transacted(() -> { - context("superuser-alex@hostsharing.net", "hs_office_partner#Fourthe.G.-forthcontact.admin"); + context("superuser-alex@hostsharing.net", "hs_office_partner#10004:Fourthe.G.-forthcontact.admin"); givenDebitor.setVatId("NEW-VAT-ID"); return debitorRepo.save(givenDebitor); }); @@ -397,7 +430,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void contactAdmin_canNotUpdateRelatedDebitor() { // given context("superuser-alex@hostsharing.net"); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth", "nin"); assertThatDebitorIsVisibleForUserWithRole( givenDebitor, "hs_office_contact#ninthcontact.admin"); @@ -448,7 +481,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void globalAdmin_canDeleteAnyDebitor() { // given context("superuser-alex@hostsharing.net", null); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "tenth", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "tenth", "Fourth", "ten"); // when final var result = jpaAttempt.transacted(() -> { @@ -468,7 +501,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { public void relatedPerson_canNotDeleteTheirRelatedDebitor() { // given context("superuser-alex@hostsharing.net", null); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele"); // when final var result = jpaAttempt.transacted(() -> { @@ -494,7 +527,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); - final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth", "Fourth"); + final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth", "Fourth", "twe"); assertThat(rawRoleRepo.findAll().size()).as("precondition failed: unexpected number of roles created") .isEqualTo(initialRoleNames.length + 5); assertThat(rawGrantRepo.findAll().size()).as("precondition failed: unexpected number of grants created") @@ -536,7 +569,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { private HsOfficeDebitorEntity givenSomeTemporaryDebitor( final String partner, final String contact, - final String bankAccount) { + final String bankAccount, + final String defaultPrefix) { return jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partner).get(0); @@ -544,10 +578,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var givenBankAccount = bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null; final var newDebitor = HsOfficeDebitorEntity.builder() - .debitorNumber(20000) + .debitorNumberSuffix((byte)20) .partner(givenPartner) .billingContact(givenContact) .refundBankAccount(givenBankAccount) + .defaultPrefix(defaultPrefix) .build(); return debitorRepo.save(newDebitor); @@ -558,7 +593,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { @AfterEach void cleanup() { context("superuser-alex@hostsharing.net"); - em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumber >= 20000").executeUpdate(); + // TODO em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20").executeUpdate(); + em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20000").executeUpdate(); } void exactlyTheseDebitorsAreReturned(final List actualResult, final String... debitorNames) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java index d9d482ba..36b3d534 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/TestHsOfficeDebitor.java @@ -9,8 +9,10 @@ import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TE @UtilityClass public class TestHsOfficeDebitor { + public byte DEFAULT_DEBITOR_SUFFIX = 0; + public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder() - .debitorNumber(10001) + .debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX) .partner(TEST_PARTNER) .billingContact(TEST_CONTACT) .build(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 79e621ee..c13a85b6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -83,7 +83,7 @@ class HsOfficeMembershipControllerAcceptanceTest { [ { "partner": { "person": { "tradeName": "First GmbH" } }, - "mainDebitor": { "debitorNumber": 10001 }, + "mainDebitor": { "debitorNumber": 1000111 }, "memberNumber": 10001, "validFrom": "2022-10-01", "validTo": null, @@ -91,7 +91,7 @@ class HsOfficeMembershipControllerAcceptanceTest { }, { "partner": { "person": { "tradeName": "Second e.K." } }, - "mainDebitor": { "debitorNumber": 10002 }, + "mainDebitor": { "debitorNumber": 1000212 }, "memberNumber": 10002, "validFrom": "2022-10-01", "validTo": null, @@ -99,7 +99,7 @@ class HsOfficeMembershipControllerAcceptanceTest { }, { "partner": { "person": { "tradeName": "Third OHG" } }, - "mainDebitor": { "debitorNumber": 10003 }, + "mainDebitor": { "debitorNumber": 1000313 }, "memberNumber": 10003, "validFrom": "2022-10-01", "validTo": null, @@ -142,6 +142,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .contentType(ContentType.JSON) .body("uuid", isUuidValid()) .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) +// .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumberSuffix())) .body("partner.person.tradeName", is("Third OHG")) .body("memberNumber", is(20001)) .body("validFrom", is("2022-10-13")) @@ -182,7 +183,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("", lenientlyEquals(""" { "partner": { "person": { "tradeName": "First GmbH" } }, - "mainDebitor": { "debitorNumber": 10001 }, + "mainDebitor": { "debitorNumber": 1000111 }, "memberNumber": 10001, "validFrom": "2022-10-01", "validTo": null, @@ -224,7 +225,7 @@ class HsOfficeMembershipControllerAcceptanceTest { RestAssured // @formatter:off .given() .header("current-user", "superuser-alex@hostsharing.net") - .header("assumed-roles", "hs_office_debitor#10003ThirdOHG-thirdcontact.agent") + .header("assumed-roles", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.agent") .port(port) .when() .get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid) @@ -235,7 +236,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "Third OHG" } }, "mainDebitor": { - "debitorNumber": 10003, + "debitorNumber": 1000313, "billingContact": { "label": "third contact" } }, "memberNumber": 10003, @@ -276,6 +277,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("uuid", isUuidValid()) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber())) +// .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumberSuffix())) .body("memberNumber", is(givenMembership.getMemberNumber())) .body("validFrom", is("2022-11-01")) .body("validTo", is("2023-12-31")) @@ -299,7 +301,7 @@ class HsOfficeMembershipControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); final var givenMembership = givenSomeTemporaryMembershipBessler(); - final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(10003).get(0); + final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(1000313).get(0); RestAssured // @formatter:off .given() @@ -318,7 +320,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .contentType(ContentType.JSON) .body("uuid", isUuidValid()) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) - .body("mainDebitor.debitorNumber", is(10003)) + .body("mainDebitor.debitorNumber", is(1000313)) .body("memberNumber", is(givenMembership.getMemberNumber())) .body("validFrom", is("2022-11-01")) .body("validTo", nullValue()) @@ -340,13 +342,13 @@ class HsOfficeMembershipControllerAcceptanceTest { @Test void partnerAgent_canViewButNotPatchValidityOfRelatedMembership() { - context.define("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.agent"); + context.define("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.agent"); final var givenMembership = givenSomeTemporaryMembershipBessler(); final var location = RestAssured // @formatter:off .given() .header("current-user", "superuser-alex@hostsharing.net") - .header("assumed-roles", "hs_office_partner#FirstGmbH-firstcontact.agent") + .header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.agent") .contentType(ContentType.JSON) .body(""" { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index 30ad5ac1..cae439f2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -27,7 +27,8 @@ class HsOfficeMembershipEntityUnitTest { void toStringContainsAllProps() { final var result = givenMembership.toString(); - assertThat(result).isEqualTo("Membership(10001, Test Ltd., 10001, [2020-01-01,))"); + assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))"); +// assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index c85a9b13..91555669 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -119,11 +119,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var all = rawRoleRepo.findAll(); assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_membership#20002FirstGmbH-firstcontact.admin", - "hs_office_membership#20002FirstGmbH-firstcontact.agent", - "hs_office_membership#20002FirstGmbH-firstcontact.guest", - "hs_office_membership#20002FirstGmbH-firstcontact.owner", - "hs_office_membership#20002FirstGmbH-firstcontact.tenant")); + "hs_office_membership#20002:FirstGmbH-firstcontact.admin", + "hs_office_membership#20002:FirstGmbH-firstcontact.agent", + "hs_office_membership#20002:FirstGmbH-firstcontact.guest", + "hs_office_membership#20002:FirstGmbH-firstcontact.owner", + "hs_office_membership#20002:FirstGmbH-firstcontact.tenant")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("GmbH-firstcontact", "")) .map(s -> s.replace("hs_office_", "")) @@ -131,32 +131,36 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { initialGrantNames, // owner - "{ grant perm * on membership#20002First to role membership#20002First.owner by system and assume }", - "{ grant role membership#20002First.owner to role global#global.admin by system and assume }", + "{ grant perm * on membership#20002:First to role membership#20002:First.owner by system and assume }", + "{ grant role membership#20002:First.owner to role global#global.admin by system and assume }", // admin - "{ grant perm edit on membership#20002First to role membership#20002First.admin by system and assume }", - "{ grant role membership#20002First.admin to role membership#20002First.owner by system and assume }", + "{ grant perm edit on membership#20002:First to role membership#20002:First.admin by system and assume }", + "{ grant role membership#20002:First.admin to role membership#20002:First.owner by system and assume }", // agent - "{ grant role membership#20002First.agent to role membership#20002First.admin by system and assume }", - "{ grant role partner#First.tenant to role membership#20002First.agent by system and assume }", - "{ grant role membership#20002First.agent to role debitor#10001First.admin by system and assume }", - "{ grant role membership#20002First.agent to role partner#First.admin by system and assume }", - "{ grant role debitor#10001First.tenant to role membership#20002First.agent by system and assume }", + "{ grant role membership#20002:First.agent to role membership#20002:First.admin by system and assume }", + "{ grant role partner#10001:First.tenant to role membership#20002:First.agent by system and assume }", + "{ grant role membership#20002:First.agent to role debitor#1000111:First.admin by system and assume }", + "{ grant role membership#20002:First.agent to role partner#10001:First.admin by system and assume }", + "{ grant role debitor#1000111:First.tenant to role membership#20002:First.agent by system and assume }", // tenant - "{ grant role membership#20002First.tenant to role membership#20002First.agent by system and assume }", - "{ grant role partner#First.guest to role membership#20002First.tenant by system and assume }", - "{ grant role debitor#10001First.guest to role membership#20002First.tenant by system and assume }", - "{ grant role membership#20002First.tenant to role debitor#10001First.agent by system and assume }", - "{ grant role membership#20002First.tenant to role partner#First.agent by system and assume }", + "{ grant role membership#20002:First.tenant to role membership#20002:First.agent by system and assume }", + "{ grant role partner#10001:First.guest to role membership#20002:First.tenant by system and assume }", +// "{ grant role debitor#1100First.guest to role membership#20002:First.tenant by system and assume }", +// "{ grant role membership#20002:First.tenant to role debitor#1100First.agent by system and assume }", + "{ grant role debitor#1000111:First.guest to role membership#20002:First.tenant by system and assume }", + "{ grant role membership#20002:First.tenant to role debitor#1000111:First.agent by system and assume }", + + "{ grant role membership#20002:First.tenant to role partner#10001:First.agent by system and assume }", // guest - "{ grant perm view on membership#20002First to role membership#20002First.guest by system and assume }", - "{ grant role membership#20002First.guest to role membership#20002First.tenant by system and assume }", - "{ grant role membership#20002First.guest to role partner#First.tenant by system and assume }", - "{ grant role membership#20002First.guest to role debitor#10001First.tenant by system and assume }", + "{ grant perm view on membership#20002:First to role membership#20002:First.guest by system and assume }", + "{ grant role membership#20002:First.guest to role membership#20002:First.tenant by system and assume }", + "{ grant role membership#20002:First.guest to role partner#10001:First.tenant by system and assume }", +// "{ grant role membership#20002:First.guest to role debitor#1100First.tenant by system and assume }", + "{ grant role membership#20002:First.guest to role debitor#1000111:First.tenant by system and assume }", null)); } @@ -181,9 +185,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // then exactlyTheseMembershipsAreReturned( result, - "Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)", - "Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)", - "Membership(10003, Third OHG, 10003, [2022-10-01,), NONE)"); + "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)", + "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)", + "Membership(10003, Third OHG, 1000313, [2022-10-01,), NONE)"); } @Test @@ -198,7 +202,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { null); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)"); } @Test @@ -210,7 +214,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)"); } } @@ -224,7 +228,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenMembership = givenSomeTemporaryMembership("First", "First"); assertThatMembershipIsVisibleForUserWithRole( givenMembership, - "hs_office_debitor#10001FirstGmbH-firstcontact.admin"); + "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin"); assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership); final var newValidityEnd = LocalDate.now(); @@ -251,13 +255,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenMembership = givenSomeTemporaryMembership("First", "First"); assertThatMembershipIsVisibleForUserWithRole( givenMembership, - "hs_office_debitor#10001FirstGmbH-firstcontact.admin"); + "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin"); assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership); final var newValidityEnd = LocalDate.now(); // when final var result = jpaAttempt.transacted(() -> { - context("superuser-alex@hostsharing.net", "hs_office_debitor#10001FirstGmbH-firstcontact.admin"); + context("superuser-alex@hostsharing.net", "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin"); givenMembership.setValidity(Range.closedOpen( givenMembership.getValidity().lower(), newValidityEnd)); return membershipRepo.save(givenMembership); @@ -325,7 +329,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // when final var result = jpaAttempt.transacted(() -> { - context("superuser-alex@hostsharing.net", "hs_office_debitor#10003ThirdOHG-thirdcontact.admin"); + context("superuser-alex@hostsharing.net", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.admin"); assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent(); membershipRepo.deleteByUuid(givenMembership.getUuid()); @@ -382,8 +386,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // then assertThat(customerLogEntries).map(Arrays::toString).contains( - "[creating Membership test-data FirstGmbH10001, hs_office_membership, INSERT]", - "[creating Membership test-data Seconde.K.10002, hs_office_membership, INSERT]"); + "[creating Membership test-data FirstGmbH11, hs_office_membership, INSERT]", + "[creating Membership test-data Seconde.K.12, hs_office_membership, INSERT]"); } @BeforeEach 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 dd1894cc..ac72d9bb 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,12 +19,8 @@ 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.sepamandate.HsOfficeSepaMandateEntity; -import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.test.JpaAttempt; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -55,11 +51,12 @@ import static org.assertj.core.api.Assertions.assertThat; * This 'test' includes the complete legacy 'office' data import. * * There is no code in 'main' because the import is not needed a normal runtime. - * There is some test data in Java resources to verfiy the data conversion. + * There is some test data in Java resources to verify the data conversion. * For a real import a main method will be added later * which reads CSV files from the file system. */ +@Disabled @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -324,14 +321,11 @@ public class ImportOfficeTables extends ContextBasedTest { final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) - .debitorNumber(rec.getInteger("member_id")) - // .memberCode(rec.get("member_code")) TODO: add as debitor_default_prefix to debitor +// .debitorNumberSuffix(rec.getByte("member_id")) +// .defaultPrefix(rec.getString("member_code")) .partner(partner) .billingContact(partner.getContact()) - // .memberRoles(toBool(rec.get("member_role")) TODO: add negated as membership_billable to membership - // .authorContract(toBool(rec.get("author_contract")) not supported - // .nonDisclosureContract(toBool(rec.get("nondisc_contract")) not supported - // .free(toBool(rec.get("free")) TODO: add negated as billable to debitor + .billable(rec.isEmpty("free")) // .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove .vatId(rec.getString("uid_vat")) @@ -345,10 +339,11 @@ public class ImportOfficeTables extends ContextBasedTest { .partner(partner) .memberNumber(rec.getInteger("member_id")) .validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) + .membershipFeeBillable(rec.isEmpty("member_role")) .reasonForTermination( isBlank(rec.getString("member_until")) ? HsOfficeReasonForTermination.NONE - : HsOfficeReasonForTermination.UNKNOWN) // TODO + : HsOfficeReasonForTermination.UNKNOWN) .mainDebitor(debitor) .build(); memberships.put(rec.getInteger("bp_id"), membership); @@ -619,9 +614,19 @@ class Record { return row[columns.indexOf(columnName)]; } + boolean isEmpty(final String columnName) { + final String value = getString(columnName); + return value == null || value.isBlank(); + } + + Byte getByte(final String columnName) { + final String value = getString(columnName); + return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0; + } + Integer getInteger(final String columnName) { final String value = getString(columnName); - return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; + return isNotBlank(value) ? Integer.valueOf(value.trim()) : 0; } BigDecimal getBigDecimal(final String columnName) { @@ -639,5 +644,4 @@ class Record { } return null; } - } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index e03d8cbe..7002685d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -105,6 +105,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newPartner = toCleanup(HsOfficePartnerEntity.builder() + .debitorNumberPrefix(22222) .person(givenPerson) .contact(givenContact) .details(HsOfficePartnerDetailsEntity.builder().build()) @@ -115,11 +116,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { // then assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin", - "hs_office_partner#ErbenBesslerMelBessler-forthcontact.agent", - "hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner", - "hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant", - "hs_office_partner#ErbenBesslerMelBessler-forthcontact.guest")); + "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.admin", + "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.agent", + "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.owner", + "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.tenant", + "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.guest")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("ErbenBesslerMelBessler", "EBess")) .map(s -> s.replace("forthcontact", "4th")) @@ -127,31 +128,31 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, // owner - "{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }", - "{ grant perm * on partner_details#EBess-4th-details to role partner#EBess-4th.owner by system and assume }", - "{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }", + "{ grant perm * on partner#22222:EBess-4th to role partner#22222:EBess-4th.owner by system and assume }", + "{ grant perm * on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.owner by system and assume }", + "{ grant role partner#22222:EBess-4th.owner to role global#global.admin by system and assume }", // admin - "{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }", - "{ grant perm edit on partner_details#EBess-4th-details to role partner#EBess-4th.admin by system and assume }", - "{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }", - "{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }", - "{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }", + "{ grant perm edit on partner#22222:EBess-4th to role partner#22222:EBess-4th.admin by system and assume }", + "{ grant perm edit on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.admin by system and assume }", + "{ grant role partner#22222:EBess-4th.admin to role partner#22222:EBess-4th.owner by system and assume }", + "{ grant role person#EBess.tenant to role partner#22222:EBess-4th.admin by system and assume }", + "{ grant role contact#4th.tenant to role partner#22222:EBess-4th.admin by system and assume }", // agent - "{ grant perm view on partner_details#EBess-4th-details to role partner#EBess-4th.agent by system and assume }", - "{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }", - "{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }", - "{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }", + "{ grant perm view on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.agent by system and assume }", + "{ grant role partner#22222:EBess-4th.agent to role partner#22222:EBess-4th.admin by system and assume }", + "{ grant role partner#22222:EBess-4th.agent to role person#EBess.admin by system and assume }", + "{ grant role partner#22222:EBess-4th.agent to role contact#4th.admin by system and assume }", // tenant - "{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }", - "{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }", - "{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }", + "{ grant role partner#22222:EBess-4th.tenant to role partner#22222:EBess-4th.agent by system and assume }", + "{ grant role person#EBess.guest to role partner#22222:EBess-4th.tenant by system and assume }", + "{ grant role contact#4th.guest to role partner#22222:EBess-4th.tenant by system and assume }", // guest - "{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }", - "{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }", + "{ grant perm view on partner#22222:EBess-4th to role partner#22222:EBess-4th.guest by system and assume }", + "{ grant role partner#22222:EBess-4th.guest to role partner#22222:EBess-4th.tenant by system and assume }", null)); } @@ -217,10 +218,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() { // given context("superuser-alex@hostsharing.net"); - final var givenPartner = givenSomeTemporaryPartnerBessler("fifth contact"); + final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "fifth contact"); assertThatPartnerIsVisibleForUserWithRole( givenPartner, - "hs_office_person#ErbenBesslerMelBessler.admin"); + "hs_office_partner#22222:ErbenBesslerMelBessler-fifthcontact.admin"); assertThatPartnerActuallyInDatabase(givenPartner); context("superuser-alex@hostsharing.net"); final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0); @@ -253,16 +254,16 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { public void partnerAgent_canNotUpdateRelatedPartner() { // given context("superuser-alex@hostsharing.net"); - final var givenPartner = givenSomeTemporaryPartnerBessler("ninth"); + final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth"); assertThatPartnerIsVisibleForUserWithRole( givenPartner, - "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); + "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent"); assertThatPartnerActuallyInDatabase(givenPartner); - final var givenNewContact = contactRepo.findContactByOptionalLabelLike("tenth").get(0); // when final var result = jpaAttempt.transacted(() -> { - context("superuser-alex@hostsharing.net", "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); + context("superuser-alex@hostsharing.net", + "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent"); givenPartner.getDetails().setBirthName("new birthname"); return partnerRepo.save(givenPartner); }); @@ -304,7 +305,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() { // given context("superuser-alex@hostsharing.net", null); - final var givenPartner = givenSomeTemporaryPartnerBessler("tenth"); + final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "tenth"); // when final var result = jpaAttempt.transacted(() -> { @@ -324,7 +325,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() { // given context("superuser-alex@hostsharing.net", null); - final var givenPartner = givenSomeTemporaryPartnerBessler("eleventh"); + final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "eleventh"); // when final var result = jpaAttempt.transacted(() -> { @@ -350,7 +351,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); - final var givenPartner = givenSomeTemporaryPartnerBessler("twelfth"); + final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "twelfth"); // when final var result = jpaAttempt.transacted(() -> { @@ -394,12 +395,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { }); } - private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final String contact) { + private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler( + final Integer debitorNumberPrefix, final String person, final String contact) { return jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); - final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); + final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var newPartner = HsOfficePartnerEntity.builder() + .debitorNumberPrefix(debitorNumberPrefix) .person(givenPerson) .contact(givenContact) .details(HsOfficePartnerDetailsEntity.builder().build()) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java index 19235167..8ac5ae85 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java @@ -8,10 +8,11 @@ import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGA public class TestHsOfficePartner { - public static final HsOfficePartnerEntity TEST_PARTNER = HsOfficePartnerWithLegalPerson("Test Ltd."); + public static final HsOfficePartnerEntity TEST_PARTNER = hsOfficePartnerWithLegalPerson("Test Ltd."); - static public HsOfficePartnerEntity HsOfficePartnerWithLegalPerson(final String tradeName) { + static public HsOfficePartnerEntity hsOfficePartnerWithLegalPerson(final String tradeName) { return HsOfficePartnerEntity.builder() + .debitorNumberPrefix(10001) .person(HsOfficePersonEntity.builder() .personType(LEGAL) .tradeName(tradeName) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 31832119..3237b3c1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -80,7 +80,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { [ { "debitor": { - "debitorNumber": 10002, + "debitorNumber": 1000212, "billingContact": { "label": "second contact" } }, "bankAccount": { "holder": "Second e.K." }, @@ -90,7 +90,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { }, { "debitor": { - "debitorNumber": 10001, + "debitorNumber": 1000111, "billingContact": { "label": "first contact" } }, "bankAccount": { "holder": "First GmbH" }, @@ -100,7 +100,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { }, { "debitor": { - "debitorNumber": 10003, + "debitorNumber": 1000313, "billingContact": { "label": "third contact" } }, "bankAccount": { "holder": "Third OHG" }, @@ -269,7 +269,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { .body("", lenientlyEquals(""" { "debitor": { - "debitorNumber": 10001, + "debitorNumber": 1000111, "billingContact": { "label": "first contact" } }, "bankAccount": { @@ -321,7 +321,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { .body("", lenientlyEquals(""" { "debitor": { - "debitorNumber": 10001, + "debitorNumber": 1000111, "billingContact": { "label": "first contact" } }, "bankAccount": { @@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: First GmbH)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); @@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { // finally, the sepaMandate is actually updated assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: First GmbH)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java index 8e5f5c79..25d0343b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateRepositoryIntegrationTest.java @@ -134,24 +134,24 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest { initialGrantNames, // owner - "{ grant perm * on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }", - "{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }", + "{ grant perm * on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }", + "{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }", // admin - "{ grant perm edit on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }", - "{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }", - "{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }", + "{ grant perm edit on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }", + "{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }", + "{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }", // agent - "{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }", - "{ grant role debitor#10001FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }", - "{ grant role sepamandate#temprefB.agent to role bankaccount#Paul....admin by system and assume }", - "{ grant role sepamandate#temprefB.agent to role debitor#10001FirstGmbH-....admin by system and assume }", + "{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }", + "{ grant role debitor#1000111:FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }", + "{ grant role sepamandate#temprefB.agent to role bankaccount#Paul....admin by system and assume }", + "{ grant role sepamandate#temprefB.agent to role debitor#1000111:FirstGmbH-....admin by system and assume }", // tenant - "{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }", - "{ grant role debitor#10001FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }", - "{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }", + "{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }", + "{ grant role debitor#1000111:FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }", + "{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }", // guest "{ grant perm view on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }", diff --git a/src/test/resources/migration/business-partners.csv b/src/test/resources/migration/business-partners.csv index 7934f551..bfe390f9 100644 --- a/src/test/resources/migration/business-partners.csv +++ b/src/test/resources/migration/business-partners.csv @@ -1,4 +1,4 @@ bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat -7;10007;mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 -10;10010;xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; -12;11012;xxx;2021-04-01;;;;;true;true;GROSS; +7;10007;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 +10;10010;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; +12;11012;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; -- 2.39.2 From 340a53a3d5a26f5744802c0238752f6f4944393e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 09:34:10 +0100 Subject: [PATCH 24/72] add column vatReverseCharge to table hs_office_debitor + view hs_office_debitor_rv --- .../office/debitor/HsOfficeDebitorEntity.java | 25 +++++++++++++++---- .../db/changelog/270-hs-office-debitor.sql | 3 ++- .../changelog/273-hs-office-debitor-rbac.sql | 1 + .../278-hs-office-debitor-test-data.sql | 4 +-- 4 files changed, 25 insertions(+), 8 deletions(-) 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 6520935d..dd5637dc 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 @@ -10,6 +10,7 @@ import net.hostsharing.hsadminng.stringify.Stringifyable; import org.hibernate.annotations.GenericGenerator; import jakarta.persistence.*; +import java.util.Optional; import java.util.UUID; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @@ -28,6 +29,7 @@ public class HsOfficeDebitorEntity implements Stringifyable { stringify(HsOfficeDebitorEntity.class, "debitor") .withProp(HsOfficeDebitorEntity::getDebitorNumber) .withProp(HsOfficeDebitorEntity::getPartner) + // TODO: add defaultPrefix? .withSeparator(": ") .quotedValues(false); @@ -59,21 +61,34 @@ public class HsOfficeDebitorEntity implements Stringifyable { @Column(name = "vatbusiness") private boolean vatBusiness; + @Column(name = "vatreversecharge") + private boolean vatReverseCharge; + @ManyToOne @JoinColumn(name = "refundbankaccountuuid") private HsOfficeBankAccountEntity refundBankAccount; + @Column(name = "defaultprefix", columnDefinition = "char(3) not null") + private String defaultPrefix; + public String getDebitorNumberString() { + // TODO: refactor + if (partner.getDebitorNumberPrefix() == null ) { + if (debitorNumberSuffix == null) { + return null; + } + return String.format("%02d", debitorNumberSuffix); + } + if (debitorNumberSuffix == null) { + return partner.getDebitorNumberPrefix() + "??"; + } return partner.getDebitorNumberPrefix() + String.format("%02d", debitorNumberSuffix); } - public int getDebitorNumber() { - return Integer.parseInt(getDebitorNumberString()); + public Integer getDebitorNumber() { + return Optional.ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null); } - @Column(name = "defaultprefix", columnDefinition = "char(3) not null") - private String defaultPrefix; - @Override public String toString() { return stringify.apply(this); diff --git a/src/main/resources/db/changelog/270-hs-office-debitor.sql b/src/main/resources/db/changelog/270-hs-office-debitor.sql index a860ab80..c11ab269 100644 --- a/src/main/resources/db/changelog/270-hs-office-debitor.sql +++ b/src/main/resources/db/changelog/270-hs-office-debitor.sql @@ -13,7 +13,8 @@ create table hs_office_debitor billingContactUuid uuid not null references hs_office_contact(uuid), vatId varchar(24), -- TODO.spec: here or in person? vatCountryCode varchar(2), - vatBusiness boolean not null, -- TODO.spec: more of such? + vatBusiness boolean not null, + vatReverseCharge boolean not null, refundBankAccountUuid uuid references hs_office_bankaccount(uuid), defaultPrefix char(3) not null constraint check_member_code check ( diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index 172ae33a..78daaea5 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -193,6 +193,7 @@ call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix vatId = new.vatId, vatCountryCode = new.vatCountryCode, vatBusiness = new.vatBusiness, + vatreversecharge = new.vatreversecharge, defaultPrefix = new.defaultPrefix $updates$); --// diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql index 3b7fb81e..74cefde0 100644 --- a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql +++ b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql @@ -38,8 +38,8 @@ begin raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact; insert - into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, refundbankaccountuuid, defaultprefix) - values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, relatedBankAccountUuid, defaultPrefix); + into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, vatreversecharge, refundbankaccountuuid, defaultprefix) + values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, false, relatedBankAccountUuid, defaultPrefix); end; $$; --// -- 2.39.2 From 5d7a245d9d21aa4413d07ee25cf048af442e2e1d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 09:35:46 +0100 Subject: [PATCH 25/72] import debitorPrefix, debitorSuffix, defaultPrefix etc. (WIP) --- build.gradle | 8 ++-- .../office/migration/ImportOfficeTables.java | 39 +++++++++++-------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/build.gradle b/build.gradle index 4268446c..2128bac1 100644 --- a/build.gradle +++ b/build.gradle @@ -129,7 +129,7 @@ openapiProcessor { processor 'io.openapiprocessor:openapi-processor-spring:2022.5' apiPath "$projectDir/src/main/resources/api-definition.yaml" mapping "$projectDir/src/main/resources/api-mappings.yaml" - targetDir "$projectDir/build/generated/sources/openapi-javax" + targetDir "$buildDir/generated/sources/openapi-javax" showWarnings true openApiNullable true } @@ -138,7 +138,7 @@ openapiProcessor { processor 'io.openapiprocessor:openapi-processor-spring:2022.5' apiPath "$projectDir/src/main/resources/api-definition/rbac/rbac.yaml" mapping "$projectDir/src/main/resources/api-definition/rbac/api-mappings.yaml" - targetDir "$projectDir/build/generated/sources/openapi-javax" + targetDir "$buildDir/generated/sources/openapi-javax" showWarnings true openApiNullable true } @@ -147,7 +147,7 @@ openapiProcessor { processor 'io.openapiprocessor:openapi-processor-spring:2022.5' apiPath "$projectDir/src/main/resources/api-definition/test/test.yaml" mapping "$projectDir/src/main/resources/api-definition/test/api-mappings.yaml" - targetDir "$projectDir/build/generated/sources/openapi-javax" + targetDir "$buildDir/generated/sources/openapi-javax" showWarnings true openApiNullable true } @@ -156,7 +156,7 @@ openapiProcessor { processor 'io.openapiprocessor:openapi-processor-spring:2022.5' apiPath "$projectDir/src/main/resources/api-definition/hs-office/hs-office.yaml" mapping "$projectDir/src/main/resources/api-definition/hs-office/api-mappings.yaml" - targetDir "$projectDir/build/generated/sources/openapi-javax" + targetDir "$buildDir/generated/sources/openapi-javax" showWarnings true openApiNullable true } 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 ac72d9bb..9c789a30 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 @@ -56,7 +56,6 @@ import static org.assertj.core.api.Assertions.assertThat; * which reads CSV files from the file system. */ -@Disabled @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -106,15 +105,15 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(contacts.toString()).isEqualTo("{}"); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(10007: null, null), - 10=debitor(10010: null, null), - 12=debitor(11012: null, null)} + 7=debitor(1000700: null, null), + 10=debitor(1001000: null, null), + 12=debitor(1101200: null, null)} """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, null, null, 10007, [2000-12-06,), NONE), - 10=Membership(10010, null, null, 10010, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, null, null, 11012, [2021-04-01,), NONE) + 7=Membership(10007, null, null, 1000700, [2000-12-06,), NONE), + 10=Membership(10010, null, null, 1001000, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, null, null, 1101200, [2021-04-01,), NONE) } """); } @@ -154,16 +153,16 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(10007: Mellies, Michael), - 10=debitor(10010: JM e.K.), - 12=debitor(11012: Test PS) + 7=debitor(1000700: Mellies, Michael), + 10=debitor(1001000: JM e.K.), + 12=debitor(1101200: Test PS) } """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, Mellies, Michael, 10007, [2000-12-06,), NONE), - 10=Membership(10010, JM e.K., 10010, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, Test PS, 11012, [2021-04-01,), NONE) + 7=Membership(10007, Mellies, Michael, 1000700, [2000-12-06,), NONE), + 10=Membership(10010, JM e.K., 1001000, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, Test PS, 1101200, [2021-04-01,), NONE) } """); } @@ -313,6 +312,7 @@ public class ImportOfficeTables extends ContextBasedTest { persons.put(rec.getInteger("bp_id"), person); final var partner = HsOfficePartnerEntity.builder() + .debitorNumberPrefix(rec.getInteger("member_id")) .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build()) .person(person) @@ -321,12 +321,12 @@ public class ImportOfficeTables extends ContextBasedTest { final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) -// .debitorNumberSuffix(rec.getByte("member_id")) -// .defaultPrefix(rec.getString("member_code")) + .debitorNumberSuffix((byte)0) + .defaultPrefix(rec.getString("member_code")) .partner(partner) .billingContact(partner.getContact()) .billable(rec.isEmpty("free")) - // .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor + .vatReverseCharge(rec.getBoolean("exempt_vat")) .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove .vatId(rec.getString("uid_vat")) .build(); @@ -624,9 +624,14 @@ class Record { return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0; } + boolean getBoolean(final String columnName) { + final String value = getString(columnName); + return isNotBlank(value) && Boolean.parseBoolean(value.trim()); + } + Integer getInteger(final String columnName) { final String value = getString(columnName); - return isNotBlank(value) ? Integer.valueOf(value.trim()) : 0; + return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; } BigDecimal getBigDecimal(final String columnName) { -- 2.39.2 From 90508df42d3bdae77d7bee22a88cc5f78d98326b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 10:30:05 +0100 Subject: [PATCH 26/72] remove hsh00- from default prefix --- .../hsadminng/hs/office/migration/ImportOfficeTables.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9c789a30..84d13415 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 @@ -322,7 +322,7 @@ public class ImportOfficeTables extends ContextBasedTest { final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) .debitorNumberSuffix((byte)0) - .defaultPrefix(rec.getString("member_code")) + .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) .billingContact(partner.getContact()) .billable(rec.isEmpty("free")) -- 2.39.2 From 5162aa0606efebaed6d51e48313ab25f8bc68c87 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 15:34:23 +0100 Subject: [PATCH 27/72] add defaultPrefix to HsOfficeDebitorEntity.toString() --- .../office/debitor/HsOfficeDebitorEntity.java | 5 ++++- .../HsOfficeDebitorEntityUnitTest.java | 3 ++- ...fficeDebitorRepositoryIntegrationTest.java | 15 +++++++------- .../office/migration/ImportOfficeTables.java | 20 +++++++++---------- ...ceSepaMandateControllerAcceptanceTest.java | 4 ++-- 5 files changed, 26 insertions(+), 21 deletions(-) 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 dd5637dc..61b8bbd8 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 @@ -25,11 +25,14 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @DisplayName("Debitor") public class HsOfficeDebitorEntity implements Stringifyable { + // TODO: I would rather like to generate something matching this example: + // debitor(1234500: Test AG, tes) + // maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)? private static Stringify stringify = stringify(HsOfficeDebitorEntity.class, "debitor") .withProp(HsOfficeDebitorEntity::getDebitorNumber) .withProp(HsOfficeDebitorEntity::getPartner) - // TODO: add defaultPrefix? + .withProp(HsOfficeDebitorEntity::getDefaultPrefix) .withSeparator(": ") .quotedValues(false); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java index 31e93492..961305f9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java @@ -22,11 +22,12 @@ class HsOfficeDebitorEntityUnitTest { .debitorNumberPrefix(12345) .build()) .billingContact(HsOfficeContactEntity.builder().label("some label").build()) + .defaultPrefix("som") .build(); final var result = given.toString(); - assertThat(result).isEqualTo("debitor(1234567: some trade name)"); + assertThat(result).isEqualTo("debitor(1234567: some trade name: som)"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index fd2e3a8d..f6dcc838 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -215,9 +215,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then allTheseDebitorsAreReturned( result, - "debitor(1000111: First GmbH)", - "debitor(1000212: Second e.K.)", - "debitor(1000313: Third OHG)"); + "debitor(1000111: First GmbH: fir)", + "debitor(1000212: Second e.K.: sec)", + "debitor(1000313: Third OHG: thi)"); } @ParameterizedTest @@ -234,7 +234,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike(null); // then: - exactlyTheseDebitorsAreReturned(result, "debitor(1000111: First GmbH)", "debitor(1000120: First GmbH)"); + exactlyTheseDebitorsAreReturned(result, + "debitor(1000111: First GmbH: fir)", + "debitor(1000120: First GmbH: fif)"); } @Test @@ -262,8 +264,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByDebitorNumber(1000313); // then -// exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)", "debitor(1000413: Fourth e.G.)"); - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG: thi)"); } } @@ -279,7 +280,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); // then - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG: thi)"); } } 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 84d13415..5c2ca134 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 @@ -56,6 +56,7 @@ import static org.assertj.core.api.Assertions.assertThat; * which reads CSV files from the file system. */ +@Disabled @DataJpaTest @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -104,11 +105,11 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(contacts.toString()).isEqualTo("{}"); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" - { - 7=debitor(1000700: null, null), - 10=debitor(1001000: null, null), - 12=debitor(1101200: null, null)} - """); + { + 7=debitor(1000700: null, null: mih), + 10=debitor(1001000: null, null: xyz), + 12=debitor(1101200: null, null: xxx)} + """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { 7=Membership(10007, null, null, 1000700, [2000-12-06,), NONE), @@ -153,9 +154,9 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(1000700: Mellies, Michael), - 10=debitor(1001000: JM e.K.), - 12=debitor(1101200: Test PS) + 7=debitor(1000700: Mellies, Michael: mih), + 10=debitor(1001000: JM e.K.: xyz), + 12=debitor(1101200: Test PS: xxx) } """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" @@ -267,7 +268,6 @@ public class ImportOfficeTables extends ContextBasedTest { coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction)); updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id"); - em.flush(); } private void updateLegacyIds(Map entities, final String legacyIdTable, final String legacyIdColumn) { @@ -321,7 +321,7 @@ public class ImportOfficeTables extends ContextBasedTest { final var debitor = HsOfficeDebitorEntity.builder() .partner(partner) - .debitorNumberSuffix((byte)0) + .debitorNumberSuffix((byte) 0) .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) .billingContact(partner.getContact()) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 3237b3c1..f84525a6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); @@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { // finally, the sepaMandate is actually updated assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); -- 2.39.2 From 76ad26b747d244b44a24b3ac76cd16d9b6d31da3 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 15:37:01 +0100 Subject: [PATCH 28/72] amend test data (Paule -> Petra) which was confusingly similar to basic test data (Paul) + cleanup --- .../changelog/203-hs-office-contact-rbac.sql | 5 +--- .../office/migration/ImportOfficeTables.java | 27 ++++++++++++------- src/test/resources/migration/contacts.csv | 2 +- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql index 39cdbf5e..7ba7891b 100644 --- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql @@ -26,9 +26,6 @@ create or replace function createRbacRolesForHsOfficeContact() returns trigger language plpgsql strict as $$ -declare - ownerRole uuid; - adminRole uuid; begin if TG_OP <> 'INSERT' then raise exception 'invalid usage of TRIGGER AFTER INSERT'; @@ -107,7 +104,7 @@ do language plpgsql $$ declare addCustomerPermissions uuid[]; globalObjectUuid uuid; - globalAdminRoleUuid uuid ; + globalAdminRoleUuid uuid; begin call defineContext('granting global new-contact permission to global admin role', null, null, null); 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 5c2ca134..6bf9ca94 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 @@ -134,7 +134,7 @@ public class ImportOfficeTables extends ContextBasedTest { { 7=partner(Mellies, Michael: Herr Michael Mellies ), 10=partner(JM e.K.: Frau Dr. Jenny Meyer , JM e.K.), - 12=partner(Test PS: Paule Schmidt , Test PS) + 12=partner(Test PS: Petra Schmidt , Test PS) } """); assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" @@ -142,14 +142,14 @@ public class ImportOfficeTables extends ContextBasedTest { 71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), 101=contact(label='Frau Dr. Jenny Meyer , JM e.K.', emailAddresses='jm@example.org'), 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@example.org'), - 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com') + 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='Paule') + 12=person(personType='UNKNOWN', tradeName='Test PS', familyName='Schmidt', givenName='Petra') } """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" @@ -242,7 +242,6 @@ public class ImportOfficeTables extends ContextBasedTest { @Test @Order(10) @Commit - //@Rollback(false) void persistEntities() { context("superuser-alex@hostsharing.net"); // TODO: use real user for actual import @@ -390,6 +389,7 @@ public class ImportOfficeTables extends ContextBasedTest { final var member = memberships.get(rec.getInteger("bp_id")); final var assetTypeMapping = new HashMap() { + { put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); @@ -402,7 +402,7 @@ public class ImportOfficeTables extends ContextBasedTest { public HsOfficeCoopAssetsTransactionType get(final String key) { final var value = super.get(key); - if ( value != null ) { + if (value != null) { return value; } throw new IllegalStateException("no mapping value found for: " + key); @@ -481,7 +481,12 @@ public class ImportOfficeTables extends ContextBasedTest { private void initContact(final HsOfficeContactEntity contact, final Record rec) { contacts.put(rec.getInteger("contact_id"), contact); - contact.setLabel(toLabel(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"), rec.getString("firma"))); + contact.setLabel(toLabel( + rec.getString("salut"), + rec.getString("title"), + rec.getString("first_name"), + rec.getString("last_name"), + rec.getString("firma"))); contact.setEmailAddresses(rec.getString("email")); contact.setPostalAddress(toAddress(rec)); contact.setPhoneNumbers(toPhoneNumbers(rec)); @@ -495,6 +500,7 @@ public class ImportOfficeTables extends ContextBasedTest { } return record; } + private String toPhoneNumbers(final Record rec) { final var result = new StringBuilder("{\n"); if (isNotBlank(rec.getString("phone_private"))) @@ -510,7 +516,11 @@ public class ImportOfficeTables extends ContextBasedTest { private String toAddress(final Record rec) { final var result = new StringBuilder(); - final var name = toName(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name")); + final var name = toName( + rec.getString("salut"), + rec.getString("title"), + rec.getString("first_name"), + rec.getString("last_name")); if (isNotBlank(name)) result.append(name + "\n"); if (isNotBlank(rec.getString("firma"))) @@ -561,7 +571,6 @@ public class ImportOfficeTables extends ContextBasedTest { return toLabel(salut, title, firstname, lastname, null); } - private Reader resourceReader(@NotNull final String resourcePath) { return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath)); } @@ -593,7 +602,7 @@ class Columns { int indexOf(final String columnName) { int index = columnNames.indexOf(columnName); - if ( index < 0 ) { + if (index < 0) { throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); } return index; diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index 341d22d4..edbd183f 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -2,4 +2,4 @@ contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zip 71; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; billing,operation 101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 8888888; ; +49 30 9999999; jm@example.org; billing 102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 9999999; am@example.org; operation -121; 12; ; Paule; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,operation +121; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,operation -- 2.39.2 From 1299b4e20aa76ad92f51c1490cc6e94faaf16454 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 15:37:38 +0100 Subject: [PATCH 29/72] increase length of hs_office_contact.label to 96 --- src/main/resources/db/changelog/200-hs-office-contact.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/db/changelog/200-hs-office-contact.sql b/src/main/resources/db/changelog/200-hs-office-contact.sql index 4bbf0bee..9b67db1b 100644 --- a/src/main/resources/db/changelog/200-hs-office-contact.sql +++ b/src/main/resources/db/changelog/200-hs-office-contact.sql @@ -7,7 +7,7 @@ create table if not exists hs_office_contact ( uuid uuid unique references RbacObject (uuid) initially deferred, - label varchar(96) not null, + label varchar(128) not null, postalAddress text, emailAddresses text, -- TODO.feat: change to json phoneNumbers text -- TODO.feat: change to json -- 2.39.2 From e9a8699aa1e0771a885b8d88a7254db27f5352b1 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 16:49:10 +0100 Subject: [PATCH 30/72] delete test data before import --- .../office/migration/ImportOfficeTables.java | 150 +++++++++++++++--- 1 file changed, 127 insertions(+), 23 deletions(-) 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 6bf9ca94..50102697 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 @@ -62,6 +62,9 @@ import static org.assertj.core.api.Assertions.assertThat; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { + // TODO: use real rbacSuperuser for actual import + private static final String rbacSuperuser = "superuser-alex@hostsharing.net"; + private static NavigableMap contacts = new TreeMap<>(); private static NavigableMap persons = new TreeMap<>(); private static NavigableMap partners = new TreeMap<>(); @@ -244,37 +247,136 @@ public class ImportOfficeTables extends ContextBasedTest { @Commit void persistEntities() { - context("superuser-alex@hostsharing.net"); // TODO: use real user for actual import + deleteTestDataFromHsOfficeTables(); + resetFromHsOfficeSequences(); + deleteFromTestTables(); + deleteFromRbacTables(); - contacts.forEach((id, contact) -> em.persist(contact)); - updateLegacyIds(contacts, "hs_office_contact_legacy_id", "contact_id"); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + contacts.forEach((id, contact) -> em.persist(contact)); + updateLegacyIds(contacts, "hs_office_contact_legacy_id", "contact_id"); + }).assertSuccessful(); - persons.forEach((id, person) -> em.persist(person)); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + persons.forEach((id, person) -> em.persist(person)); + }).assertSuccessful(); - partners.forEach((id, partner) -> em.persist(partner)); - updateLegacyIds(partners, "hs_office_partner_legacy_id", "bp_id"); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + partners.forEach((id, partner) -> em.persist(partner)); + updateLegacyIds(partners, "hs_office_partner_legacy_id", "bp_id"); + }).assertSuccessful(); - debitors.forEach((id, debitor) -> em.persist(debitor)); - memberships.forEach((id, membership) -> em.persist(membership)); - bankAccounts.forEach((id, account) -> em.persist(account)); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + debitors.forEach((id, debitor) -> em.persist(debitor)); + }).assertSuccessful(); - sepaMandates.forEach((id, mandate) -> em.persist(mandate)); - updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + memberships.forEach((id, membership) -> em.persist(membership)); - coopShares.forEach((id, shareTransaction) -> em.persist(shareTransaction)); - updateLegacyIds(coopShares, "hs_office_coopsharestransaction_legacy_id", "member_share_id"); + }).assertSuccessful(); - coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction)); - updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id"); + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + bankAccounts.forEach((id, account) -> em.persist(account)); + + }).assertSuccessful(); + + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + sepaMandates.forEach((id, mandate) -> em.persist(mandate)); + updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); + + }).assertSuccessful(); + + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + coopShares.forEach((id, shareTransaction) -> em.persist(shareTransaction)); + updateLegacyIds(coopShares, "hs_office_coopsharestransaction_legacy_id", "member_share_id"); + + }).assertSuccessful(); + + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction)); + updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id"); + }).assertSuccessful(); } - private void updateLegacyIds(Map entities, final String legacyIdTable, final String legacyIdColumn) { + private void deleteTestDataFromHsOfficeTables() { + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + em.createNativeQuery("DELETE FROM hs_office_relationship WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_coopassetstransaction WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_coopassetstransaction_legacy_id WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_coopsharestransaction WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_coopsharestransaction_legacy_id WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_membership WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_sepamandate WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_sepamandate_legacy_id WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_debitor WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_bankaccount WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_partner WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_partner_details WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_contact WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM hs_office_person WHERE true").executeUpdate(); + }).assertSuccessful(); + } + + private void resetFromHsOfficeSequences() { + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + em.createNativeQuery("ALTER SEQUENCE hs_office_contact_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + em.createNativeQuery("ALTER SEQUENCE hs_office_coopassetstransaction_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + em.createNativeQuery("ALTER SEQUENCE public.hs_office_coopsharestransaction_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + em.createNativeQuery("ALTER SEQUENCE public.hs_office_partner_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + em.createNativeQuery("ALTER SEQUENCE public.hs_office_sepamandate_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + }); + } + + private void deleteFromTestTables() { + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + em.createNativeQuery("DELETE FROM test_domain WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM test_package WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM test_customer WHERE true").executeUpdate(); + }).assertSuccessful(); + } + + private void deleteFromRbacTables() { + jpaAttempt.transacted(() -> { + context(rbacSuperuser); + final var usersNotToDelete = em.createNativeQuery("SELECT name FROM rbacuser WHERE name like 'superuser-%'").getResultList(); + final var usersToDelete = em.createNativeQuery("SELECT name FROM rbacuser WHERE name not like 'superuser-%'").getResultList(); + System.getenv(); + }); + + jpaAttempt.transacted(() -> { + context(rbacSuperuser); +// em.createNativeQuery("DELETE FROM rbacobject WHERE objecttable like 'hs_%'").executeUpdate(); +// em.createNativeQuery("DELETE FROM rbacgrants WHERE true").executeUpdate(); +// em.createNativeQuery("DELETE FROM rbacpermission WHERE true").executeUpdate(); +// em.createNativeQuery("DELETE FROM rbacreference WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM rbacuser_rv WHERE name not like 'superuser-%'").executeUpdate(); + em.createNativeQuery("DELETE FROM tx_journal WHERE true").executeUpdate(); + em.createNativeQuery("DELETE FROM tx_context WHERE true").executeUpdate(); + }).assertSuccessful(); + } + + private void updateLegacyIds( + Map entities, + final String legacyIdTable, + final String legacyIdColumn) { entities.forEach((id, entity) -> em.createNativeQuery(""" - UPDATE ${legacyIdTable} - SET ${legacyIdColumn} = :legacyId - WHERE uuid = :uuid - """ + UPDATE ${legacyIdTable} + SET ${legacyIdColumn} = :legacyId + WHERE uuid = :uuid + """ .replace("${legacyIdTable}", legacyIdTable) .replace("${legacyIdColumn}", legacyIdColumn)) .setParameter("legacyId", id) @@ -337,7 +439,9 @@ public class ImportOfficeTables extends ContextBasedTest { final var membership = HsOfficeMembershipEntity.builder() .partner(partner) .memberNumber(rec.getInteger("member_id")) - .validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) + .validity(toPostgresDateRange( + rec.getLocalDate("member_since"), + rec.getLocalDate("member_until"))) .membershipFeeBillable(rec.isEmpty("member_role")) .reasonForTermination( isBlank(rec.getString("member_until")) @@ -366,10 +470,10 @@ public class ImportOfficeTables extends ContextBasedTest { .transactionType( "SUBSCRIPTION".equals(rec.getString("action")) ? HsOfficeCoopSharesTransactionType.SUBSCRIPTION - : "UNSUBSCRIPTION".equals(rec.getString("action")) + : "UNSUBSCRIPTION".equals(rec.getString("action")) ? HsOfficeCoopSharesTransactionType.CANCELLATION : HsOfficeCoopSharesTransactionType.ADJUSTMENT - ) + ) .shareCount(rec.getInteger("quantity")) .reference(rec.getString("comment")) .build(); -- 2.39.2 From 70621fd482c770540392f38ea9ecc53549f6ad3b Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 16:49:36 +0100 Subject: [PATCH 31/72] always use jdbc:postgresql://localhost:5432/postgres for ImportTestData --- .../hsadminng/hs/office/migration/ImportOfficeTables.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 50102697..b5c30035 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 @@ -57,7 +57,7 @@ import static org.assertj.core.api.Assertions.assertThat; */ @Disabled -@DataJpaTest +@DataJpaTest(properties = "spring.datasource.url=jdbc:postgresql://localhost:5432/postgres") @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { -- 2.39.2 From db7c10169148292ea77dd2fb1ac1a09983ac68e7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 11 Jan 2024 18:05:46 +0100 Subject: [PATCH 32/72] Liquibase-Scripts and database migration possible with explicit (non-superuser) DB-User --- .../resources/db/changelog/010-context.sql | 12 ++++----- .../resources/db/changelog/050-rbac-base.sql | 25 +++++++++++-------- .../db/changelog/051-rbac-user-grant.sql | 2 +- .../db/changelog/054-rbac-context.sql | 8 +++--- .../resources/db/changelog/055-rbac-views.sql | 8 +++--- .../db/changelog/058-rbac-generators.sql | 4 +-- .../db/changelog/080-rbac-global.sql | 6 ++--- .../db/changelog/123-test-package-rbac.sql | 2 +- .../db/changelog/133-test-domain-rbac.sql | 2 +- .../office/migration/ImportOfficeTables.java | 23 ++++++++++++++--- .../mapper/PostgresArrayIntegrationTest.java | 6 ++--- 11 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/main/resources/db/changelog/010-context.sql b/src/main/resources/db/changelog/010-context.sql index 71559b7b..4820cf9c 100644 --- a/src/main/resources/db/changelog/010-context.sql +++ b/src/main/resources/db/changelog/010-context.sql @@ -55,7 +55,7 @@ end; $$; */ create or replace function currentTask() returns varchar(96) - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentTask varchar(96); @@ -83,7 +83,7 @@ end; $$; */ create or replace function currentRequest() returns varchar(512) - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentRequest varchar(512); @@ -107,7 +107,7 @@ end; $$; */ create or replace function currentUser() returns varchar(63) - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentUser varchar(63); @@ -131,7 +131,7 @@ end; $$; */ create or replace function assumedRoles() returns varchar(63)[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentSubject varchar(63); @@ -214,7 +214,7 @@ end ; $$; create or replace function currentSubjects() returns varchar(63)[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare assumedRoles varchar(63)[]; @@ -229,7 +229,7 @@ end; $$; create or replace function hasAssumedRole() returns boolean - stable leakproof + stable -- leakproof language plpgsql as $$ begin return array_length(assumedRoles(), 1) > 0; diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index 3b395d33..fea69fe9 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -208,7 +208,7 @@ create type RbacRoleDescriptor as create or replace function roleDescriptor(objectTable varchar(63), objectUuid uuid, roleType RbacRoleType) returns RbacRoleDescriptor returns null on null input - stable leakproof + stable -- leakproof language sql as $$ select objectTable, objectUuid, roleType::RbacRoleType; $$; @@ -432,7 +432,7 @@ $$; create or replace function findPermissionId(forObjectUuid uuid, forOp RbacOp) returns uuid returns null on null input - stable leakproof + stable -- leakproof language sql as $$ select uuid from RbacPermission p @@ -515,7 +515,7 @@ end; $$; create or replace function isPermissionGrantedToSubject(permissionId uuid, subjectId uuid) returns BOOL - stable leakproof + stable -- leakproof language sql as $$ select exists( select * @@ -537,7 +537,7 @@ $$; create or replace function hasGlobalRoleGranted(userUuid uuid) returns bool - stable leakproof + stable -- leakproof language sql as $$ select exists( select r.uuid @@ -758,13 +758,18 @@ $$; -- ============================================================================ ---changeset rbac-base-PGSQL-ROLES:1 endDelimiter:--// +--changeset rbac-base-PGSQL-ROLES:1 context:dev,tc endDelimiter:--// -- ---------------------------------------------------------------------------- -create role admin; -grant all privileges on all tables in schema public to admin; - -create role restricted; -grant all privileges on all tables in schema public to restricted; +do $$ + begin + if '${ADMIN_USER}'='admin' then + create role admin; + grant all privileges on all tables in schema public to admin; + create role restricted; + grant all privileges on all tables in schema public to restricted; + end if; + end $$ --// + diff --git a/src/main/resources/db/changelog/051-rbac-user-grant.sql b/src/main/resources/db/changelog/051-rbac-user-grant.sql index 81cadc94..23dcbdd4 100644 --- a/src/main/resources/db/changelog/051-rbac-user-grant.sql +++ b/src/main/resources/db/changelog/051-rbac-user-grant.sql @@ -6,7 +6,7 @@ create or replace function assumedRoleUuid() returns uuid - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentSubjectsUuids uuid[]; diff --git a/src/main/resources/db/changelog/054-rbac-context.sql b/src/main/resources/db/changelog/054-rbac-context.sql index 32a652a6..ede86057 100644 --- a/src/main/resources/db/changelog/054-rbac-context.sql +++ b/src/main/resources/db/changelog/054-rbac-context.sql @@ -7,7 +7,7 @@ create or replace function determineCurrentUserUuid(currentUser varchar) returns uuid - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentUserUuid uuid; @@ -25,7 +25,7 @@ end; $$; create or replace function determineCurrentSubjectsUuids(currentUserUuid uuid, assumedRoles varchar) returns uuid[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare roleName text; @@ -116,7 +116,7 @@ end; $$; create or replace function currentUserUuid() returns uuid - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentUserUuid text; @@ -150,7 +150,7 @@ end; $$; */ create or replace function currentSubjectsUuids() returns uuid[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare currentSubjectsUuids text; diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql index 68ea11b5..054b6df1 100644 --- a/src/main/resources/db/changelog/055-rbac-views.sql +++ b/src/main/resources/db/changelog/055-rbac-views.sql @@ -41,7 +41,7 @@ select * ) as unordered -- @formatter:on order by objectTable || '#' || objectIdName || '.' || roleType; -grant all privileges on rbacrole_rv to restricted; +grant all privileges on rbacrole_rv to ${RESTRICTED_USER}; --// @@ -126,7 +126,7 @@ select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || join RbacObject as o on o.uuid = r.objectUuid order by grantedRoleIdName; -- @formatter:on -grant all privileges on rbacrole_rv to restricted; +grant all privileges on rbacrole_rv to ${RESTRICTED_USER}; --// @@ -240,7 +240,7 @@ create or replace view RbacUser_rv as ) as unordered -- @formatter:on order by unordered.name; -grant all privileges on RbacUser_rv to restricted; +grant all privileges on RbacUser_rv to ${RESTRICTED_USER}; --// -- ============================================================================ @@ -326,7 +326,7 @@ select r.uuid as roleuuid, p.uuid as permissionUuid, join rbacgrants g on g.ascendantuuid = r.uuid join rbacpermission p on p.uuid = g.descendantuuid join rbacobject o on o.uuid = p.objectuuid; -grant all privileges on RbacOwnGrantedPermissions_rv to restricted; +grant all privileges on RbacOwnGrantedPermissions_rv to ${RESTRICTED_USER}; -- @formatter:om -- ============================================================================ diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql index 82685028..70dfc021 100644 --- a/src/main/resources/db/changelog/058-rbac-generators.sql +++ b/src/main/resources/db/changelog/058-rbac-generators.sql @@ -104,7 +104,7 @@ begin create or replace view %1$s_iv as select target.uuid, cleanIdentifier(%2$s) as idName from %1$s as target; - grant all privileges on %1$s_iv to restricted; + grant all privileges on %1$s_iv to ${RESTRICTED_USER}; $sql$, targetTable, idNameExpression); execute sql; @@ -157,7 +157,7 @@ begin from %1$s as target where target.uuid in (select * from accessibleObjects) order by %2$s; - grant all privileges on %1$s_rv to restricted; + grant all privileges on %1$s_rv to ${RESTRICTED_USER}; $sql$, targetTable, orderBy); execute sql; diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql index deb690c0..e2141f55 100644 --- a/src/main/resources/db/changelog/080-rbac-global.sql +++ b/src/main/resources/db/changelog/080-rbac-global.sql @@ -18,7 +18,7 @@ create table Global ); create unique index Global_Singleton on Global ((0)); -grant select on global to restricted; +grant select on global to ${RESTRICTED_USER}; --// @@ -48,7 +48,7 @@ drop view if exists global_iv; create or replace view global_iv as select target.uuid, target.name as idName from global as target; -grant all privileges on global_iv to restricted; +grant all privileges on global_iv to ${RESTRICTED_USER}; /* Returns the objectUuid for a given identifying name (in this case the idName). @@ -99,7 +99,7 @@ commit; create or replace function globalAdmin() returns RbacRoleDescriptor returns null on null input - stable leakproof + stable -- leakproof language sql as $$ select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType; $$; diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql index f4eb2edd..d97f6148 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -93,7 +93,7 @@ call generateRbacIdentityView('test_package', 'target.name'); -- from test_package as target -- where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_package', currentSubjectsUuids())) -- order by target.name; --- grant all privileges on test_package_rv to restricted; +-- grant all privileges on test_package_rv to ${RESTRICTED_USER}; call generateRbacRestrictedView('test_package', 'target.name', $updates$ diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql index ceeb5de3..fcd5e4b7 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.sql +++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql @@ -110,5 +110,5 @@ create or replace view test_domain_rv as select target.* from test_domain as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectsUuids())); -grant all privileges on test_domain_rv to restricted; +grant all privileges on test_domain_rv to ${RESTRICTED_USER}; --// 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 b5c30035..bbb1efd4 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 @@ -54,10 +54,27 @@ import static org.assertj.core.api.Assertions.assertThat; * There is some test data in Java resources to verify the data conversion. * For a real import a main method will be added later * which reads CSV files from the file system. + * + * When run on a Hostsharing database, it needs the following settings (hsh99_... just examples): + * + * CREATE USER hsh99_admin WITH PASSWORD 'password'; + * GRANT ALL ON SCHEMA public TO hsh99_admin; + * + * CREATE USER hsh99_restricted WITH PASSWORD 'password'; + * GRANT ALL PRIVILEGES ON ALL TALBES IN SCHEMA hsh99_hsadminng to hsh99_restricted + * + * CREATE EXTENSION "uuid-ossp"; + * + * And the environment variables ADMIN_USER and RESTRICTED_USER have to be set to the actual users. + * TODO: password */ - -@Disabled -@DataJpaTest(properties = "spring.datasource.url=jdbc:postgresql://localhost:5432/postgres") +// @Disabled +@DataJpaTest(properties = { + "spring.profiles.active=migration", + "spring.datasource.url=jdbc:postgresql://localhost:5432/postgres", + "spring.datasource.username=hsh99_admin", + "spring.datasource.password=password" +}) @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { diff --git a/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java index c76141b1..8f3e95e0 100644 --- a/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/mapper/PostgresArrayIntegrationTest.java @@ -22,7 +22,7 @@ class PostgresArrayIntegrationTest { em.createNativeQuery(""" create or replace function returnEmptyArray() returns text[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare emptyArray text[] = '{}'; @@ -42,7 +42,7 @@ class PostgresArrayIntegrationTest { em.createNativeQuery(""" create or replace function returnStringArray() returns varchar(63)[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare text1 text = 'one'; @@ -65,7 +65,7 @@ class PostgresArrayIntegrationTest { em.createNativeQuery(""" create or replace function returnUuidArray() returns uuid[] - stable leakproof + stable -- leakproof language plpgsql as $$ declare uuid1 UUID = 'f47ac10b-58cc-4372-a567-0e02b2c3d479'; -- 2.39.2 From 2f5acd41715d175b17815bb72b93142bb88dfbe4 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 12 Jan 2024 18:41:16 +0100 Subject: [PATCH 33/72] use env variables for admin+restricted user, jdbc-url and admin-password --- .aliases | 3 + .../office/contact/HsOfficeContactEntity.java | 4 +- src/main/resources/application.yml | 4 ++ .../db/changelog/009-environment-defaults.sql | 12 ++++ .../resources/db/changelog/050-rbac-base.sql | 10 ++-- .../resources/db/changelog/055-rbac-views.sql | 8 +-- .../db/changelog/058-rbac-generators.sql | 4 +- .../db/changelog/080-rbac-global.sql | 4 +- .../db/changelog/123-test-package-rbac.sql | 2 +- .../db/changelog/133-test-domain-rbac.sql | 2 +- .../office/migration/ImportOfficeTables.java | 60 +++++++++++++++---- .../RbacRoleControllerAcceptanceTest.java | 10 ++++ .../RbacUserControllerAcceptanceTest.java | 9 +++ src/test/resources/migration/contacts.csv | 9 +-- 14 files changed, 109 insertions(+), 32 deletions(-) create mode 100644 src/main/resources/db/changelog/009-environment-defaults.sql diff --git a/.aliases b/.aliases index 9477474d..cc2b341a 100644 --- a/.aliases +++ b/.aliases @@ -1,3 +1,6 @@ +export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin +export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted + gradleWrapper () { if [ ! -f gradlew ]; then echo "No 'gradlew' found. Maybe you are not in the root dir of a gradle project?" diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java index 012379b9..c3ecb6be 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java @@ -39,10 +39,10 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid { private String postalAddress; @Column(name = "emailaddresses", columnDefinition = "json") - private String emailAddresses; + private String emailAddresses; // TODO: check if we can really add multiple. format: ["eins@...", "zwei@..."] @Column(name = "phonenumbers", columnDefinition = "json") - private String phoneNumbers; + private String phoneNumbers; // TODO: check if we can really add multiple. format: { "office": "+49 40 12345-10", "fax": "+49 40 12345-05" } @Override public String toString() { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 120e98c3..8d928aeb 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,3 +20,7 @@ spring: liquibase: contexts: dev + +hsadminng: + postgres: + leakproof: diff --git a/src/main/resources/db/changelog/009-environment-defaults.sql b/src/main/resources/db/changelog/009-environment-defaults.sql new file mode 100644 index 00000000..fa69a98b --- /dev/null +++ b/src/main/resources/db/changelog/009-environment-defaults.sql @@ -0,0 +1,12 @@ +--liquibase formatted sql + + +-- ============================================================================ +-- NUMERIC-HASH-FUNCTIONS +--changeset hash:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +create function postgresAdminUserName() returns text as $$ +if +$$ language sql; +--// diff --git a/src/main/resources/db/changelog/050-rbac-base.sql b/src/main/resources/db/changelog/050-rbac-base.sql index fea69fe9..3254b464 100644 --- a/src/main/resources/db/changelog/050-rbac-base.sql +++ b/src/main/resources/db/changelog/050-rbac-base.sql @@ -763,13 +763,15 @@ $$; do $$ begin - if '${ADMIN_USER}'='admin' then + if '${HSADMINNG_POSTGRES_ADMIN_USERNAME}'='admin' then create role admin; grant all privileges on all tables in schema public to admin; - - create role restricted; - grant all privileges on all tables in schema public to restricted; end if; + + if '${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}'='restricted' then + create role restricted; + end if; + -- grant all privileges on all tables in schema public to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; end $$ --// diff --git a/src/main/resources/db/changelog/055-rbac-views.sql b/src/main/resources/db/changelog/055-rbac-views.sql index 054b6df1..d1d1d926 100644 --- a/src/main/resources/db/changelog/055-rbac-views.sql +++ b/src/main/resources/db/changelog/055-rbac-views.sql @@ -41,7 +41,7 @@ select * ) as unordered -- @formatter:on order by objectTable || '#' || objectIdName || '.' || roleType; -grant all privileges on rbacrole_rv to ${RESTRICTED_USER}; +grant all privileges on rbacrole_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// @@ -126,7 +126,7 @@ select o.objectTable || '#' || findIdNameByObjectUuid(o.objectTable, o.uuid) || join RbacObject as o on o.uuid = r.objectUuid order by grantedRoleIdName; -- @formatter:on -grant all privileges on rbacrole_rv to ${RESTRICTED_USER}; +grant all privileges on rbacrole_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// @@ -240,7 +240,7 @@ create or replace view RbacUser_rv as ) as unordered -- @formatter:on order by unordered.name; -grant all privileges on RbacUser_rv to ${RESTRICTED_USER}; +grant all privileges on RbacUser_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// -- ============================================================================ @@ -326,7 +326,7 @@ select r.uuid as roleuuid, p.uuid as permissionUuid, join rbacgrants g on g.ascendantuuid = r.uuid join rbacpermission p on p.uuid = g.descendantuuid join rbacobject o on o.uuid = p.objectuuid; -grant all privileges on RbacOwnGrantedPermissions_rv to ${RESTRICTED_USER}; +grant all privileges on RbacOwnGrantedPermissions_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; -- @formatter:om -- ============================================================================ diff --git a/src/main/resources/db/changelog/058-rbac-generators.sql b/src/main/resources/db/changelog/058-rbac-generators.sql index 70dfc021..fa198308 100644 --- a/src/main/resources/db/changelog/058-rbac-generators.sql +++ b/src/main/resources/db/changelog/058-rbac-generators.sql @@ -104,7 +104,7 @@ begin create or replace view %1$s_iv as select target.uuid, cleanIdentifier(%2$s) as idName from %1$s as target; - grant all privileges on %1$s_iv to ${RESTRICTED_USER}; + grant all privileges on %1$s_iv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; $sql$, targetTable, idNameExpression); execute sql; @@ -157,7 +157,7 @@ begin from %1$s as target where target.uuid in (select * from accessibleObjects) order by %2$s; - grant all privileges on %1$s_rv to ${RESTRICTED_USER}; + grant all privileges on %1$s_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; $sql$, targetTable, orderBy); execute sql; diff --git a/src/main/resources/db/changelog/080-rbac-global.sql b/src/main/resources/db/changelog/080-rbac-global.sql index e2141f55..034400fa 100644 --- a/src/main/resources/db/changelog/080-rbac-global.sql +++ b/src/main/resources/db/changelog/080-rbac-global.sql @@ -18,7 +18,7 @@ create table Global ); create unique index Global_Singleton on Global ((0)); -grant select on global to ${RESTRICTED_USER}; +grant select on global to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// @@ -48,7 +48,7 @@ drop view if exists global_iv; create or replace view global_iv as select target.uuid, target.name as idName from global as target; -grant all privileges on global_iv to ${RESTRICTED_USER}; +grant all privileges on global_iv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; /* Returns the objectUuid for a given identifying name (in this case the idName). diff --git a/src/main/resources/db/changelog/123-test-package-rbac.sql b/src/main/resources/db/changelog/123-test-package-rbac.sql index d97f6148..8a2fd857 100644 --- a/src/main/resources/db/changelog/123-test-package-rbac.sql +++ b/src/main/resources/db/changelog/123-test-package-rbac.sql @@ -93,7 +93,7 @@ call generateRbacIdentityView('test_package', 'target.name'); -- from test_package as target -- where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_package', currentSubjectsUuids())) -- order by target.name; --- grant all privileges on test_package_rv to ${RESTRICTED_USER}; +-- grant all privileges on test_package_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; call generateRbacRestrictedView('test_package', 'target.name', $updates$ diff --git a/src/main/resources/db/changelog/133-test-domain-rbac.sql b/src/main/resources/db/changelog/133-test-domain-rbac.sql index fcd5e4b7..89b63018 100644 --- a/src/main/resources/db/changelog/133-test-domain-rbac.sql +++ b/src/main/resources/db/changelog/133-test-domain-rbac.sql @@ -110,5 +110,5 @@ create or replace view test_domain_rv as select target.* from test_domain as target where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'domain', currentSubjectsUuids())); -grant all privileges on test_domain_rv to ${RESTRICTED_USER}; +grant all privileges on test_domain_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}; --// 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 bbb1efd4..803daae3 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 @@ -22,6 +22,7 @@ import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -55,30 +56,45 @@ import static org.assertj.core.api.Assertions.assertThat; * For a real import a main method will be added later * which reads CSV files from the file system. * - * When run on a Hostsharing database, it needs the following settings (hsh99_... just examples): + * When run on a Hostsharing database, it needs the following settings (hsh99_... just examples). + * + * In a real hostsharing environment, these are created via (the old) hsadmin: * * CREATE USER hsh99_admin WITH PASSWORD 'password'; - * GRANT ALL ON SCHEMA public TO hsh99_admin; + * CREATE DATABASE hsh99_hsadminng ENCODING 'UTF8' TEMPLATE template0; + * REVOKE ALL ON DATABASE hsh99_hsadminng FROM public; -- why does hsadmin do that? + * ALTER DATABASE hsh99_hsadminng OWNER TO hsh99_admin; * * CREATE USER hsh99_restricted WITH PASSWORD 'password'; - * GRANT ALL PRIVILEGES ON ALL TALBES IN SCHEMA hsh99_hsadminng to hsh99_restricted + * GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to hsh99_restricted; + + * Additionally we need these settings (because the Hostsharing DB-Admin has no CREATE right): * - * CREATE EXTENSION "uuid-ossp"; + * CREATE EXTENSION IF EXISTS "uuid-ossp"; * - * And the environment variables ADMIN_USER and RESTRICTED_USER have to be set to the actual users. - * TODO: password + * Then these environment variables need to be set for Liquibase: + * export HSADMIN_POSTGRES_JDBC=jdbc:postgresql://localhost:6432/hsh99_hsadminng + * export HSADMIN_POSTGRES_ADMIN_USERNAME=hsh99_admin + * export HSADMIN_POSTGRES_ADMIN_PASSWORD=password + * export HSADMIN_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted + * + * Then, to run the import, uncomment the @Diabled and then run: + * + * gw test -tests ImportOfficeTables -x check */ -// @Disabled +//@Disabled @DataJpaTest(properties = { - "spring.profiles.active=migration", - "spring.datasource.url=jdbc:postgresql://localhost:5432/postgres", - "spring.datasource.username=hsh99_admin", - "spring.datasource.password=password" + "spring.datasource.url=${HSADMIN_POSTGRES_JDBC:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", + "spring.datasource.username=${HSADMIN_POSTGRES_ADMIN_USERNAME:admin}", + "spring.datasource.password=${HSADMIN_POSTGRES_ADMIN_PASSWORD:password}" }) @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { + @Value("${spring.datasource.url}") + private String jdbcUrl; + // TODO: use real rbacSuperuser for actual import private static final String rbacSuperuser = "superuser-alex@hostsharing.net"; @@ -115,6 +131,10 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } + if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + return; + } + // no contacts yet => mostly null values assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { @@ -150,6 +170,10 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } + if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + return; + } + assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { 7=partner(Mellies, Michael: Herr Michael Mellies ), @@ -199,6 +223,10 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } + if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + return; + } + assertThat(bankAccounts.toString()).isEqualToIgnoringWhitespace(""" { 234234=bankAccount(holder='Michael Mellies', iban='DE37500105177419788228', bic='INGDDEFFXXX'), @@ -224,6 +252,10 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } + if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + return; + } + assertThat(coopShares.toString()).isEqualToIgnoringWhitespace(""" { 33443=CoopShareTransaction(10007, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), @@ -245,6 +277,10 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } + if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + return; + } + assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace(""" { 30000=CoopAssetsTransaction(10007, 2000-12-06, DEPOSIT, 1280.00, for subscription A), @@ -442,7 +478,7 @@ public class ImportOfficeTables extends ContextBasedTest { .debitorNumberSuffix((byte) 0) .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) - .billingContact(partner.getContact()) + .billingContact(partner.getContact()) // TODO falsch .billable(rec.isEmpty("free")) .vatReverseCharge(rec.getBoolean("exempt_vat")) .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java index abed40ac..389b0f12 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacrole/RbacRoleControllerAcceptanceTest.java @@ -7,12 +7,14 @@ import net.hostsharing.hsadminng.rbac.rbacuser.RbacUserRepository; import net.hostsharing.test.Accepts; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.*; @SpringBootTest( @@ -37,6 +39,14 @@ class RbacRoleControllerAcceptanceTest { @Autowired RbacRoleRepository rbacRoleRepository; + @Value("${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}") + String restrictedUser; + + @Test + void testEnv() { + assertThat(restrictedUser).isEqualTo("restricted"); + } + @Test @Accepts({ "ROL:L(List)" }) void globalAdmin_withoutAssumedRole_canViewAllRoles() { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java index 76d738ad..9f2fd68e 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java @@ -9,6 +9,7 @@ import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.transaction.annotation.Transactional; @@ -42,6 +43,14 @@ class RbacUserControllerAcceptanceTest { @Autowired RbacUserRepository rbacUserRepository; + @Value("${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}") + String restrictedUser; + + @Test + void testEnv() { + assertThat(restrictedUser).isEqualTo("restricted"); + } + @Nested class CreateRbacUser { diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index edbd183f..1d8cc3e8 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,5 +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; billing,operation -101; 10; Frau; Jenny; Meyer; Dr.; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 8888888; ; +49 30 9999999; jm@example.org; billing -102; 10; Herr; Andrew; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 9999999; am@example.org; operation -121; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,operation +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@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@example.org; operation +102; 10; Herr; Philip; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm@example.org; contractual +121; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,contractual,operation -- 2.39.2 From 033a86f10d929e0387eefa2a6eb4f7140ea5fa38 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 13 Jan 2024 21:27:09 +0100 Subject: [PATCH 34/72] separately import contact as billing or contractual contact --- .../office/migration/ImportOfficeTables.java | 26 ++++++++++++------- src/test/resources/migration/contacts.csv | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) 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 803daae3..7be9d809 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 @@ -170,7 +170,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { return; } @@ -183,11 +183,12 @@ public class ImportOfficeTables extends ContextBasedTest { """); 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@example.org'), - 102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@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 , 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') + } """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" { @@ -223,7 +224,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { return; } @@ -252,7 +253,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { return; } @@ -619,6 +620,7 @@ public class ImportOfficeTables extends ContextBasedTest { if (isNotBlank(rec.getString("roles")) && rec.getString("roles").contains("billing")) { final var partner = partners.get(rec.getInteger("bp_id")); + final var debitor = debitors.get(rec.getInteger("bp_id")); final var person = partner.getPerson(); person.setTradeName(rec.getString("firma")); @@ -626,7 +628,13 @@ public class ImportOfficeTables extends ContextBasedTest { person.setGivenName(rec.getString("first_name")); person.setFamilyName(rec.getString("last_name")); - initContact(partner.getContact(), rec); + 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); + } } else { initContact(new HsOfficeContactEntity(), rec); diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index 1d8cc3e8..040a7c75 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@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@example.org; operation -102; 10; Herr; Philip; Meyer; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm@example.org; contractual +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 121; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; billing,contractual,operation -- 2.39.2 From ff2671ff928ff0a401b44a25d01987f8fd2973fc Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 15 Jan 2024 16:31:29 +0100 Subject: [PATCH 35/72] environment scripting for import-office-tables --- .aliases | 20 +++++- build.gradle | 6 ++ .../office/migration/ImportOfficeTables.java | 63 +++++++++++-------- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/.aliases b/.aliases index cc2b341a..455bcd79 100644 --- a/.aliases +++ b/.aliases @@ -1,4 +1,8 @@ +# For using the alias import-office-tables, # copy these exports to .environment (ignored by git) +# and amend them according to your external DB: +export HSADMINNG_POSTGRES_JDBC_URL= export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin +export HSADMINNG_POSTGRES_ADMIN_PASSWORD= export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted gradleWrapper () { @@ -39,9 +43,22 @@ postgresAutodoc () { dot -Tsvg build/postgres-autodoc.neato >build/postgres-autodoc-rbac.svg && \ echo "generated $PWD/build/postgres-autodoc-rbac.svg" } - alias postgres-autodoc=postgresAutodoc +function importOfficeTables() { + export HSADMINNG_POSTGRES_JDBC=jdbc:postgresql://localhost:5432/postgres + export HSADMINNG_POSTGRES_ADMIN_USERNAME=hsh99_admin + export HSADMINNG_POSTGRES_ADMIN_PASSWORD=password + export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted + + if [ -f .environment ]; then + source .environment + fi + + ./gradlew test --tests ImportOfficeTables -x pitest +} +alias import-office-tables=importOfficeTables + alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' alias podman-use='export DOCKER_HOST="unix:///run/user/$UID/podman/podman.sock"; export TESTCONTAINERS_RYUK_DISABLED=true' @@ -56,3 +73,4 @@ alias pg-sql-backup='docker exec -i hsadmin-ng-postgres /usr/bin/pg_dump --clean alias pg-sql-restore='gunzip --stdout | docker exec -i hsadmin-ng-postgres psql -U postgres -d postgres' alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l' + diff --git a/build.gradle b/build.gradle index 2128bac1..dff765fc 100644 --- a/build.gradle +++ b/build.gradle @@ -237,6 +237,12 @@ test { excludes = [ 'net.hostsharing.hsadminng.**.generated.**', ] + + test { + systemProperty "my_variable", System.getenv("MY_VARIABLE") + } + + } jacocoTestReport { dependsOn test 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 7be9d809..c29a2c94 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 @@ -58,35 +58,42 @@ import static org.assertj.core.api.Assertions.assertThat; * * When run on a Hostsharing database, it needs the following settings (hsh99_... just examples). * - * In a real hostsharing environment, these are created via (the old) hsadmin: - * - * CREATE USER hsh99_admin WITH PASSWORD 'password'; - * CREATE DATABASE hsh99_hsadminng ENCODING 'UTF8' TEMPLATE template0; - * REVOKE ALL ON DATABASE hsh99_hsadminng FROM public; -- why does hsadmin do that? - * ALTER DATABASE hsh99_hsadminng OWNER TO hsh99_admin; - * - * CREATE USER hsh99_restricted WITH PASSWORD 'password'; - * GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to hsh99_restricted; + * In a real Hostsharing environment, these are created via (the old) hsadmin: - * Additionally we need these settings (because the Hostsharing DB-Admin has no CREATE right): + CREATE USER hsh99_admin WITH PASSWORD 'password'; + CREATE DATABASE hsh99_hsadminng ENCODING 'UTF8' TEMPLATE template0; + REVOKE ALL ON DATABASE hsh99_hsadminng FROM public; -- why does hsadmin do that? + ALTER DATABASE hsh99_hsadminng OWNER TO hsh99_admin; + + CREATE USER hsh99_restricted WITH PASSWORD 'password'; + + \c hsh99_hsadminng + + GRANT ALL PRIVILEGES ON SCHEMA public to hsh99_admin; + + * Additionally, we need these settings (because the Hostsharing DB-Admin has no CREATE right): + + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + + -- maybe something like that is needed for the 2nd user + -- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to hsh99_restricted; + + * Then copy this to a file named .environment (excluded from git): + + export HSADMINNG_POSTGRES_JDBC_URL=jdbc:postgresql://localhost:6432/hsh99_hsadminng + export HSADMINNG_POSTGRES_ADMIN_USERNAME=hsh99_admin + export HSADMINNG_POSTGRES_ADMIN_PASSWORD=password + export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted + + * Then, import the office data, uncomment the @Diabled and then run: * - * CREATE EXTENSION IF EXISTS "uuid-ossp"; - * - * Then these environment variables need to be set for Liquibase: - * export HSADMIN_POSTGRES_JDBC=jdbc:postgresql://localhost:6432/hsh99_hsadminng - * export HSADMIN_POSTGRES_ADMIN_USERNAME=hsh99_admin - * export HSADMIN_POSTGRES_ADMIN_PASSWORD=password - * export HSADMIN_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted - * - * Then, to run the import, uncomment the @Diabled and then run: - * - * gw test -tests ImportOfficeTables -x check + * import-office-tables # comes from .aliases file */ -//@Disabled +@Disabled @DataJpaTest(properties = { - "spring.datasource.url=${HSADMIN_POSTGRES_JDBC:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", - "spring.datasource.username=${HSADMIN_POSTGRES_ADMIN_USERNAME:admin}", - "spring.datasource.password=${HSADMIN_POSTGRES_ADMIN_PASSWORD:password}" + "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", + "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:admin}", + "spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}" }) @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -120,6 +127,12 @@ public class ImportOfficeTables extends ContextBasedTest { @MockBean HttpServletRequest request; + @Test + @Order(0) + void preconditions() { + assertThat(jdbcUrl).isEqualTo("jdbc:postgresql://localhost:5432/postgres"); + } + @Test @Order(1) void importBusinessPartners() { -- 2.39.2 From 2f3e4038e4b7fcc825092a26f6427df3a9e8d3be Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 15 Jan 2024 16:58:07 +0100 Subject: [PATCH 36/72] remove precondition --- .gitignore | 1 + doc/hs-office-data-structure.md | 8 ++++++++ .../hs/office/migration/ImportOfficeTables.java | 13 +++++-------- 3 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 doc/hs-office-data-structure.md diff --git a/.gitignore b/.gitignore index 9500b657..10ebda13 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,4 @@ Desktop.ini # ESLint ###################### .eslintcache +/.environment diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md new file mode 100644 index 00000000..3f9b5462 --- /dev/null +++ b/doc/hs-office-data-structure.md @@ -0,0 +1,8 @@ +```mermaid + +flowchart TB + +direction TB + +Relationship-->Person +``` 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 c29a2c94..f5ea4489 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 @@ -18,6 +18,7 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; 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.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.*; @@ -89,7 +90,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * import-office-tables # comes from .aliases file */ -@Disabled +//@Disabled @DataJpaTest(properties = { "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:admin}", @@ -110,6 +111,8 @@ public class ImportOfficeTables extends ContextBasedTest { private static NavigableMap partners = new TreeMap<>(); private static NavigableMap debitors = new TreeMap<>(); private static NavigableMap memberships = new TreeMap<>(); + + private static NavigableMap relationships = new TreeMap<>(); private static NavigableMap sepaMandates = new TreeMap<>(); private static NavigableMap bankAccounts = new TreeMap<>(); private static NavigableMap coopShares = new TreeMap<>(); @@ -127,12 +130,6 @@ public class ImportOfficeTables extends ContextBasedTest { @MockBean HttpServletRequest request; - @Test - @Order(0) - void preconditions() { - assertThat(jdbcUrl).isEqualTo("jdbc:postgresql://localhost:5432/postgres"); - } - @Test @Order(1) void importBusinessPartners() { @@ -492,7 +489,7 @@ public class ImportOfficeTables extends ContextBasedTest { .debitorNumberSuffix((byte) 0) .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) - .billingContact(partner.getContact()) // TODO falsch + //.billingContact(partner.getContact()) // TODO falsch .billable(rec.isEmpty("free")) .vatReverseCharge(rec.getBoolean("exempt_vat")) .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove -- 2.39.2 From cd7ea891c27e2c12957950c8132f2bedb3555da0 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 15 Jan 2024 17:01:25 +0100 Subject: [PATCH 37/72] revert accidental comment about billingContact --- .../hsadminng/hs/office/migration/ImportOfficeTables.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f5ea4489..84f6cb3c 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 @@ -489,7 +489,7 @@ public class ImportOfficeTables extends ContextBasedTest { .debitorNumberSuffix((byte) 0) .defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .partner(partner) - //.billingContact(partner.getContact()) // TODO falsch + .billingContact(partner.getContact()) // TODO falsch .billable(rec.isEmpty("free")) .vatReverseCharge(rec.getBoolean("exempt_vat")) .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove -- 2.39.2 From 3a66a28a33d741d7978f09b45c8be9d84ae20de7 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 09:53:39 +0100 Subject: [PATCH 38/72] 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 -- 2.39.2 From 19f962cf2e1c6e1d20f2fa432ea68217d5fc60e9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 14:24:46 +0100 Subject: [PATCH 39/72] HasUUid for all Entity classes and migrate SOLE_AGENT+JOINT_AGENT to REPRESENTATIVE --- .../HsOfficeRelationshipType.java | 4 +- .../hs-office-relationship-schemas.yaml | 3 +- .../changelog/230-hs-office-relationship.sql | 2 +- .../238-hs-office-relationship-test-data.sql | 8 ++-- ...eRelationshipControllerAcceptanceTest.java | 14 +++---- ...ficeRelationshipEntityPatcherUnitTest.java | 2 +- ...RelationshipRepositoryIntegrationTest.java | 42 +++++++++---------- 7 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java index 7a5097a3..980cc04f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java @@ -1,8 +1,8 @@ package net.hostsharing.hsadminng.hs.office.relationship; public enum HsOfficeRelationshipType { - SOLE_AGENT, - JOINT_AGENT, + UNKNOWN, + REPRESENTATIVE, ACCOUNTING_CONTACT, TECHNICAL_CONTACT } diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml index 23af0ff0..ed4111bd 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml @@ -6,8 +6,7 @@ components: HsOfficeRelationshipType: type: string enum: - - SOLE_AGENT # e.g. CEO - - JOINT_AGENT # e.g. heir + - REPRESENTATIVE - ACCOUNTING_CONTACT - TECHNICAL_CONTACT diff --git a/src/main/resources/db/changelog/230-hs-office-relationship.sql b/src/main/resources/db/changelog/230-hs-office-relationship.sql index 6e9e5961..2efca56a 100644 --- a/src/main/resources/db/changelog/230-hs-office-relationship.sql +++ b/src/main/resources/db/changelog/230-hs-office-relationship.sql @@ -4,7 +4,7 @@ --changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficeRelationshipType AS ENUM ('SOLE_AGENT', 'JOINT_AGENT', 'CO_OWNER', 'ACCOUNTING_CONTACT', 'TECHNICAL_CONTACT'); +CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'REPRESENTATIVE', 'ACCOUNTING_CONTACT', 'TECHNICAL_CONTACT'); CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT; diff --git a/src/main/resources/db/changelog/238-hs-office-relationship-test-data.sql b/src/main/resources/db/changelog/238-hs-office-relationship-test-data.sql index 1e8e40c0..a46cf7ce 100644 --- a/src/main/resources/db/changelog/238-hs-office-relationship-test-data.sql +++ b/src/main/resources/db/changelog/238-hs-office-relationship-test-data.sql @@ -58,7 +58,7 @@ begin select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person; select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact; - call createHsOfficeRelationshipTestData(person.uuid, contact.uuid, 'SOLE_AGENT'); + call createHsOfficeRelationshipTestData(person.uuid, contact.uuid, 'REPRESENTATIVE'); commit; end loop; end; $$; @@ -71,11 +71,11 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeRelationshipTestData('First GmbH', 'Smith', 'SOLE_AGENT', 'first contact'); + call createHsOfficeRelationshipTestData('First GmbH', 'Smith', 'REPRESENTATIVE', 'first contact'); - call createHsOfficeRelationshipTestData('Second e.K.', 'Smith', 'SOLE_AGENT', 'second contact'); + call createHsOfficeRelationshipTestData('Second e.K.', 'Smith', 'REPRESENTATIVE', 'second contact'); - call createHsOfficeRelationshipTestData('Third OHG', 'Smith', 'SOLE_AGENT', 'third contact'); + call createHsOfficeRelationshipTestData('Third OHG', 'Smith', 'REPRESENTATIVE', 'third contact'); end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java index f090295b..60f393c1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java @@ -75,7 +75,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { .port(port) .when() .get("http://localhost/api/hs/office/relationships?personUuid=%s&relationshipType=%s" - .formatted(givenPerson.getUuid(), HsOfficeRelationshipTypeResource.SOLE_AGENT)) + .formatted(givenPerson.getUuid(), HsOfficeRelationshipTypeResource.REPRESENTATIVE)) .then().log().all().assertThat() .statusCode(200) .contentType("application/json") @@ -91,7 +91,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "givenName": "Peter", "familyName": "Smith" }, - "relType": "SOLE_AGENT", + "relType": "REPRESENTATIVE", "contact": { "label": "third contact" } }, { @@ -106,7 +106,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "givenName": "Peter", "familyName": "Smith" }, - "relType": "SOLE_AGENT", + "relType": "REPRESENTATIVE", "contact": { "label": "second contact" } }, { @@ -120,7 +120,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "givenName": "Peter", "familyName": "Smith" }, - "relType": "SOLE_AGENT", + "relType": "REPRESENTATIVE", "contact": { "label": "first contact" } } ] @@ -387,7 +387,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { .statusCode(200) .contentType(ContentType.JSON) .body("uuid", isUuidValid()) - .body("relType", is("JOINT_AGENT")) + .body("relType", is("REPRESENTATIVE")) .body("relAnchor.tradeName", is("Erben Bessler")) .body("relHolder.familyName", is("Winkler")) .body("contact.label", is("forth contact")); @@ -400,7 +400,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { assertThat(rel.getRelAnchor().getTradeName()).contains("Bessler"); assertThat(rel.getRelHolder().getFamilyName()).contains("Winkler"); assertThat(rel.getContact().getLabel()).isEqualTo("forth contact"); - assertThat(rel.getRelType()).isEqualTo(HsOfficeRelationshipType.JOINT_AGENT); + assertThat(rel.getRelType()).isEqualTo(HsOfficeRelationshipType.REPRESENTATIVE); return true; }); } @@ -477,7 +477,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { final var givenContact = contactRepo.findContactByOptionalLabelLike("seventh contact").get(0); final var newRelationship = HsOfficeRelationshipEntity.builder() .uuid(UUID.randomUUID()) - .relType(HsOfficeRelationshipType.JOINT_AGENT) + .relType(HsOfficeRelationshipType.REPRESENTATIVE) .relAnchor(givenAnchorPerson) .relHolder(givenHolderPerson) .contact(givenContact) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java index c3537764..1c12a629 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityPatcherUnitTest.java @@ -52,7 +52,7 @@ class HsOfficeRelationshipEntityPatcherUnitTest extends PatchUnitTestBase< protected HsOfficeRelationshipEntity newInitialEntity() { final var entity = new HsOfficeRelationshipEntity(); entity.setUuid(INITIAL_RELATIONSHIP_UUID); - entity.setRelType(HsOfficeRelationshipType.SOLE_AGENT); + entity.setRelType(HsOfficeRelationshipType.REPRESENTATIVE); entity.setRelAnchor(givenInitialAnchorPerson); entity.setRelHolder(givenInitialHolderPerson); entity.setContact(givenInitialContact); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java index b76175d9..326227ff 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java @@ -77,7 +77,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { final var newRelationship = toCleanup(HsOfficeRelationshipEntity.builder() .relAnchor(givenAnchorPerson) .relHolder(givenHolderPerson) - .relType(HsOfficeRelationshipType.JOINT_AGENT) + .relType(HsOfficeRelationshipType.REPRESENTATIVE) .contact(givenContact) .build()); return relationshipRepo.save(newRelationship); @@ -105,7 +105,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { final var newRelationship = toCleanup(HsOfficeRelationshipEntity.builder() .relAnchor(givenAnchorPerson) .relHolder(givenHolderPerson) - .relType(HsOfficeRelationshipType.JOINT_AGENT) + .relType(HsOfficeRelationshipType.REPRESENTATIVE) .contact(givenContact) .build()); return relationshipRepo.save(newRelationship); @@ -114,26 +114,26 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { // then assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.admin", - "hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.owner", - "hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant")); + "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin", + "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner", + "hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm * on hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.owner by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.owner to role global#global.admin by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }", + "{ grant perm * on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role global#global.admin by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner to role hs_office_person#BesslerAnita.admin by system and assume }", - "{ grant perm edit on hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.admin by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.admin to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.owner by system and assume }", + "{ grant perm edit on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.owner by system and assume }", - "{ grant perm view on hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_contact#forthcontact.admin by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }", + "{ grant perm view on hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_contact#forthcontact.admin by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }", - "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.admin by system and assume }", - "{ grant role hs_office_contact#forthcontact.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }", - "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }", + "{ grant role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.admin by system and assume }", + "{ grant role hs_office_contact#forthcontact.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", + "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-REPRESENTATIVE-BesslerAnita.tenant by system and assume }", null) ); } @@ -159,9 +159,9 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { // then allTheseRelationshipsAreReturned( result, - "rel(relAnchor='First GmbH', relType='SOLE_AGENT', relHolder='Smith, Peter', contact='first contact')", - "rel(relAnchor='Third OHG', relType='SOLE_AGENT', relHolder='Smith, Peter', contact='third contact')", - "rel(relAnchor='Second e.K.', relType='SOLE_AGENT', relHolder='Smith, Peter', contact='second contact')"); + "rel(relAnchor='First GmbH', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='first contact')", + "rel(relAnchor='Third OHG', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='third contact')", + "rel(relAnchor='Second e.K.', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='second contact')"); } @Test @@ -176,7 +176,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { // then: exactlyTheseRelationshipsAreReturned( result, - "rel(relAnchor='First GmbH', relType='SOLE_AGENT', relHolder='Smith, Peter', contact='first contact')"); + "rel(relAnchor='First GmbH', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='first contact')"); } } @@ -392,7 +392,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { final var givenHolderPerson = personRepo.findPersonByOptionalNameLike(holderPerson).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var newRelationship = HsOfficeRelationshipEntity.builder() - .relType(HsOfficeRelationshipType.JOINT_AGENT) + .relType(HsOfficeRelationshipType.REPRESENTATIVE) .relAnchor(givenAnchorPerson) .relHolder(givenHolderPerson) .contact(givenContact) -- 2.39.2 From 683c2f0ce47c196aadf2dbb5dcfbd85b77ef7137 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 14:44:17 +0100 Subject: [PATCH 40/72] enable import of a single contact for multiple roles --- .../office/migration/ImportOfficeTables.java | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) 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 b4ce3a47..0657275e 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 @@ -49,7 +49,6 @@ 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. @@ -102,8 +101,8 @@ import static org.assertj.core.api.Fail.fail; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportOfficeTables extends ContextBasedTest { - @Value("${spring.datasource.url}") - private String jdbcUrl; + @Value("${spring.datasource.username}") + private String postgresAdminUser; // TODO: use real rbacSuperuser for actual import private static final String rbacSuperuser = "superuser-alex@hostsharing.net"; @@ -145,16 +144,16 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + if ( !"admin".equals(postgresAdminUser) ) { return; } // no contacts yet => mostly null values assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { - 7=partner(null, null: null), - 10=partner(null, null: null), - 12=partner(null, null: null) + 7=partner(null, null), + 10=partner(null, null), + 12=partner(null, null) } """); assertThat(contacts.toString()).isEqualTo("{}"); @@ -184,7 +183,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { + if ( !"admin".equals(postgresAdminUser) ) { return; } @@ -245,7 +244,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { + if ( !"admin".equals(postgresAdminUser) ) { return; } @@ -274,7 +273,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("HSADMINNG_POSTGRES_ADMIN_USERNAME") )) { + if ( !"admin".equals(postgresAdminUser) ) { return; } @@ -299,7 +298,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(System.getenv("ADMIN_USER") )) { + if ( !"admin".equals(postgresAdminUser) ) { return; } @@ -657,7 +656,6 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - if (isNotBlank(rec.getString("roles"))) { final var contactId = rec.getInteger("contact_id"); final var partner = partners.get(rec.getInteger("bp_id")); @@ -672,11 +670,16 @@ public class ImportOfficeTables extends ContextBasedTest { final var contact = HsOfficeContactEntity.builder().build(); contacts.put(contactId, initContact(contact, rec)); + var imported = false; if (rec.getString("roles").contains("contractual")) { + assertThat(partner.getContact()).isNull(); partner.setContact(contact); + imported = true; } if (rec.getString("roles").contains("billing")) { + assertThat(debitor.getBillingContact()).isNull(); debitor.setBillingContact(contact); + imported = true; } if (rec.getString("roles").contains("operation")) { final var rel = HsOfficeRelationshipEntity.builder() @@ -686,9 +689,26 @@ public class ImportOfficeTables extends ContextBasedTest { .relType(HsOfficeRelationshipType.TECHNICAL_CONTACT) .build(); relationships.put(contactId, rel); + imported = true; } - } else { - fail("contact without role: " + rec); + if (rec.getString("roles").contains("representative")) { + final var rel = HsOfficeRelationshipEntity.builder() + .relAnchor(partner.getPerson()) + .relHolder(person) + .contact(contact) + .relType(HsOfficeRelationshipType.REPRESENTATIVE) + .build(); + relationships.put(contactId, rel); + imported = true; + } + if (!imported) { + final var rel = HsOfficeRelationshipEntity.builder() + .relAnchor(partner.getPerson()) + .relHolder(person) + .contact(contact) + .relType(HsOfficeRelationshipType.UNKNOWN) + .build(); + relationships.put(contactId, rel); } }); } -- 2.39.2 From 2e609884bf032bf1b8497cba680cf65f73487db9 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 15:46:56 +0100 Subject: [PATCH 41/72] remove _CONTACT from relationship types --- .../office/relationship/HsOfficeRelationshipType.java | 4 ++-- .../hs-office/hs-office-relationship-schemas.yaml | 4 ++-- .../db/changelog/230-hs-office-relationship.sql | 2 +- .../HsOfficeRelationshipControllerAcceptanceTest.java | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java index 980cc04f..0535ca67 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java @@ -3,6 +3,6 @@ package net.hostsharing.hsadminng.hs.office.relationship; public enum HsOfficeRelationshipType { UNKNOWN, REPRESENTATIVE, - ACCOUNTING_CONTACT, - TECHNICAL_CONTACT + ACCOUNTING, + OPERATIONS } diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml index ed4111bd..cfda18c0 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml @@ -7,8 +7,8 @@ components: type: string enum: - REPRESENTATIVE - - ACCOUNTING_CONTACT - - TECHNICAL_CONTACT + - ACCOUNTING + - TECHNICAL HsOfficeRelationship: type: object diff --git a/src/main/resources/db/changelog/230-hs-office-relationship.sql b/src/main/resources/db/changelog/230-hs-office-relationship.sql index 2efca56a..826b7bba 100644 --- a/src/main/resources/db/changelog/230-hs-office-relationship.sql +++ b/src/main/resources/db/changelog/230-hs-office-relationship.sql @@ -4,7 +4,7 @@ --changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'REPRESENTATIVE', 'ACCOUNTING_CONTACT', 'TECHNICAL_CONTACT'); +CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'REPRESENTATIVE', 'ACCOUNTING', 'OPERATIONS'); CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java index 60f393c1..bbfa43ff 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipControllerAcceptanceTest.java @@ -153,7 +153,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "contactUuid": "%s" } """.formatted( - HsOfficeRelationshipTypeResource.ACCOUNTING_CONTACT, + HsOfficeRelationshipTypeResource.ACCOUNTING, givenAnchorPerson.getUuid(), givenHolderPerson.getUuid(), givenContact.getUuid())) @@ -164,7 +164,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { .statusCode(201) .contentType(ContentType.JSON) .body("uuid", isUuidValid()) - .body("relType", is("ACCOUNTING_CONTACT")) + .body("relType", is("ACCOUNTING")) .body("relAnchor.tradeName", is("Third OHG")) .body("relHolder.givenName", is("Paul")) .body("contact.label", is("forth contact")) @@ -197,7 +197,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "contactUuid": "%s" } """.formatted( - HsOfficeRelationshipTypeResource.ACCOUNTING_CONTACT, + HsOfficeRelationshipTypeResource.ACCOUNTING, givenAnchorPersonUuid, givenHolderPerson.getUuid(), givenContact.getUuid())) @@ -230,7 +230,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "contactUuid": "%s" } """.formatted( - HsOfficeRelationshipTypeResource.ACCOUNTING_CONTACT, + HsOfficeRelationshipTypeResource.ACCOUNTING, givenAnchorPerson.getUuid(), givenHolderPersonUuid, givenContact.getUuid())) @@ -263,7 +263,7 @@ class HsOfficeRelationshipControllerAcceptanceTest { "contactUuid": "%s" } """.formatted( - HsOfficeRelationshipTypeResource.ACCOUNTING_CONTACT, + HsOfficeRelationshipTypeResource.ACCOUNTING, givenAnchorPerson.getUuid(), givenHolderPerson.getUuid(), givenContactUuid)) -- 2.39.2 From 1e5bc034072f0558bb49979ae62efcd295a74b26 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 15:49:53 +0100 Subject: [PATCH 42/72] make contacts.csv easier readable by empty lines, comments and better numbering --- .../office/migration/ImportOfficeTables.java | 53 +++++++++++++------ src/test/resources/migration/contacts.csv | 16 ++++-- 2 files changed, 48 insertions(+), 21 deletions(-) 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 0657275e..bcbee321 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 @@ -35,9 +35,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.constraints.NotNull; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.math.BigDecimal; import java.nio.file.Files; import java.nio.file.Path; @@ -45,10 +43,12 @@ import java.time.LocalDate; import java.util.*; import static java.util.Arrays.stream; +import static java.util.Objects.requireNonNull; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; 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. @@ -196,11 +196,11 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" { - 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') + 1101=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), + 1201=contact(label='Frau Dr. Jenny Meyer-Billing , JM e.K.', emailAddresses='jm-billing@example.org'), + 1202=contact(label='Herr Andrew Meyer-Operation , JM e.K.', emailAddresses='am-operation@example.org'), + 1203=contact(label='Herr Philip Meyer-Contract , JM e.K.', emailAddresses='pm-partner@example.org'), + 1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') } """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" @@ -226,9 +226,10 @@ public class ImportOfficeTables extends ContextBasedTest { """); 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') + 1101=rel(relAnchor='Mellies, Michael', relType='OPERATIONS', relHolder='Mellies, Michael', contact='Herr Michael Mellies '), + 1202=rel(relAnchor='JM e.K.', relType='OPERATIONS', relHolder='JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), + 1203=rel(relAnchor='JM e.K.', relType='REPRESENTATIVE', relHolder='JM e.K.', contact='Herr Philip Meyer-Contract , JM e.K.'), + 1301=rel(relAnchor='Test PS', relType='REPRESENTATIVE', relHolder='Test PS', contact='Petra Schmidt , Test PS') } """); } @@ -485,12 +486,28 @@ public class ImportOfficeTables extends ContextBasedTest { .withQuoteChar('"') .build(); - try (CSVReader csvReader = new CSVReaderBuilder(reader) + final var filteredReader = skippingEmptyAndCommentLines(reader); + try (CSVReader csvReader = new CSVReaderBuilder(filteredReader) .withCSVParser(parser) .build()) { return csvReader.readAll(); } } + public static Reader skippingEmptyAndCommentLines(Reader reader) throws IOException { + try (var bufferedReader = new BufferedReader(reader); + StringWriter writer = new StringWriter()) { + + String line; + while ((line = bufferedReader.readLine()) != null) { + if (!line.isBlank() && !line.startsWith("#")) { + writer.write(line); + writer.write("\n"); + } + } + + return new StringReader(writer.toString()); + } + } private void importBusinessPartners(final String[] header, final List records) { @@ -658,6 +675,10 @@ public class ImportOfficeTables extends ContextBasedTest { .forEach(rec -> { final var contactId = rec.getInteger("contact_id"); + if (rec.getString("roles").isBlank()) { + fail("empty roles assignment not allowed for contact_id: " + contactId); + } + final var partner = partners.get(rec.getInteger("bp_id")); final var debitor = debitors.get(rec.getInteger("bp_id")); @@ -671,7 +692,7 @@ public class ImportOfficeTables extends ContextBasedTest { contacts.put(contactId, initContact(contact, rec)); var imported = false; - if (rec.getString("roles").contains("contractual")) { + if (rec.getString("roles").contains("partner")) { assertThat(partner.getContact()).isNull(); partner.setContact(contact); imported = true; @@ -686,12 +707,12 @@ public class ImportOfficeTables extends ContextBasedTest { .relAnchor(partner.getPerson()) .relHolder(person) .contact(contact) - .relType(HsOfficeRelationshipType.TECHNICAL_CONTACT) + .relType(HsOfficeRelationshipType.OPERATIONS) .build(); relationships.put(contactId, rel); imported = true; } - if (rec.getString("roles").contains("representative")) { + if (rec.getString("roles").contains("contractual")) { final var rel = HsOfficeRelationshipEntity.builder() .relAnchor(partner.getPerson()) .relHolder(person) @@ -808,7 +829,7 @@ public class ImportOfficeTables extends ContextBasedTest { } private Reader resourceReader(@NotNull final String resourcePath) { - return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath)); + return new InputStreamReader(requireNonNull(getClass().getClassLoader().getResourceAsStream(resourcePath))); } private Reader fileReader(@NotNull final Path filePath) throws IOException { diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index aec7aea7..1dfd6e24 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,6 +1,12 @@ 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-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 + +# eine natürliche Person +1101; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; partner,billing,operation + +# eine juristische Person mit drei separaten Ansprechpartnern +1201; 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 +1202; 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 +1203; 10; Herr; Philip; Meyer-Contract; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual + +# eine juristische Person mit nur einem Ansprechpartner +1301; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation -- 2.39.2 From 1512e0e1a54738065e4c35d183c8afa137b23276 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 16 Jan 2024 18:44:01 +0100 Subject: [PATCH 43/72] first version of functional data model of office module --- doc/hs-office-data-structure.md | 149 +++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 3 deletions(-) diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md index 3f9b5462..bc7b04e5 100644 --- a/doc/hs-office-data-structure.md +++ b/doc/hs-office-data-structure.md @@ -1,8 +1,151 @@ ```mermaid +classDiagram + direction RL -flowchart TB + namespace Geschäftspartner { + class partner-MeierGmbH + class partnerDetails-MeierGmbH + class contact-MeierGmbH + class person-MeierGmbH + + class debitor-MeierGmbH + class contact-MeierGmbH-Buha + + class person-FrankMeier + class contact-FrankMeier + class rel-MeierGmbH-FrankMeier -direction TB + class person-SabineMeier + class contact-SabineMeier + class rel-MeierGmbH-SabineMeier + } + + class partner-MeierGmbH { + +person + +contact + +details + } + partner-MeierGmbH o-- person-MeierGmbH + partner-MeierGmbH o-- contact-MeierGmbH + partner-MeierGmbH *-- partnerDetails-MeierGmbH -Relationship-->Person + class partnerDetails-MeierGmbH { + +registrationOffice + +registrationNumber + +birthName + +dateOfDeath + } + + class person-MeierGmbH { + + personType: LEGAL + + tradeName: Meier GmbH + + familyName + + givenName + } + + class contact-MeierGmbH { + +postalAddress: Hauptstraße 5, 22345 Hamburg + +phoneNumbers: +49 40 12345-00 + +emailAddresses: office@meier-gmbh.de + } + + class debitor-MeierGmbH { + +Partner partner + +Numeric(2) debitorNumberSuffix: 00 + +Contact billingContact + +boolean billable: true + +String vatId: ID123456789 + +String vatCountryCode: DE + +boolean vatBusiness: true + +boolean vatReverseCharge: false + +BankAccount refundBankAccount + +String defaultPrefix: mei + } + debitor-MeierGmbH o-- partner-MeierGmbH + debitor-MeierGmbH o-- contact-MeierGmbH-Buha + + class contact-MeierGmbH-Buha { + +postalAddress: Hauptstraße 5, 22345 Hamburg + +phoneNumbers: +49 40 12345-05 + +emailAddresses: buha@meier-gmbh.de + } + + class person-FrankMeier { + + personType: NATURAL + + tradeName + + familyName: Meier + + givenName: Frank + } + + class contact-FrankMeier { + +postalAddress + +phoneNumbers: +49 40 12345-22 + +emailAddresses: sabine.meier@meier-gmbh.de + } + + class rel-MeierGmbH-FrankMeier { + +RelType relType REPRESENTATIVE + +Person relAnchor + +Person relHolder + +Contact contact + } + rel-MeierGmbH-FrankMeier o-- person-MeierGmbH : relAnchor + rel-MeierGmbH-FrankMeier o-- person-FrankMeier : relHolder + rel-MeierGmbH-FrankMeier o-- contact-FrankMeier + + class person-SabineMeier { + +personType: NATURAL + +tradeName + +familyName: Meier + +givenName: Sabine + } + + class contact-SabineMeier { + +postalAddress + +phoneNumbers: +49 40 12345-22 + +emailAddresses: sabine.meier@meier-gmbh.de + } + + class rel-MeierGmbH-SabineMeier { + +RelType relType OPERATIONAL + +Person relAnchor + +Person relHolder + +Contact contact + } + rel-MeierGmbH-SabineMeier o-- person-MeierGmbH : relAnchor + rel-MeierGmbH-SabineMeier o-- person-SabineMeier : relHolder + rel-MeierGmbH-SabineMeier o-- contact-SabineMeier + + namespace Enums { + + class RelType { + <> + UNKNOWN + REPRESENTATIVE + ACCOUNTING + OPERATIONS + } + + class PersonType { + <> + UNKNOWN + NATURAL + LEGAL + SOLE_REPRESENTATION + JOINT_REPRESENTATION + } + + } +``` + +```mermaid +classDiagram + classA --|> classB : Inheritance + classC --* classD : Composition + classE --o classF : Aggregation + classG --> classH : Association + classI -- classJ : Link(Solid) + classK ..> classL : Dependency + classM ..|> classN : Realization + classO .. classP : Link(Dashed) ``` -- 2.39.2 From 31141ad4c33c801705faef64305c8232baa84ce6 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 17 Jan 2024 18:25:35 +0100 Subject: [PATCH 44/72] PartnerDetails: add birthplace, and improve toString and introduce @Tag("import") --- .aliases | 2 + build.gradle | 19 +++- doc/hs-office-data-structure.md | 103 +++++++++--------- .../office/debitor/HsOfficeDebitorEntity.java | 2 +- .../partner/HsOfficePartnerDetailsEntity.java | 4 +- .../office/person/HsOfficePersonEntity.java | 3 +- .../db/changelog/009-check-environment.sql | 18 +++ .../db/changelog/009-environment-defaults.sql | 12 -- .../db/changelog/220-hs-office-partner.sql | 1 + .../224-hs-office-partner-details-rbac.sql | 1 + .../db/changelog/db.changelog-master.yaml | 2 + .../HsOfficeDebitorEntityUnitTest.java | 4 +- ...fficeDebitorRepositoryIntegrationTest.java | 14 +-- ...iceMembershipControllerAcceptanceTest.java | 4 +- .../HsOfficeMembershipEntityUnitTest.java | 3 +- ...ceMembershipRepositoryIntegrationTest.java | 10 +- .../office/migration/ImportOfficeTables.java | 85 ++++++++++----- .../HsOfficePartnerDetailsEntityUnitTest.java | 3 +- .../HsOfficePartnerEntityUnitTest.java | 15 ++- ...fficePartnerRepositoryIntegrationTest.java | 10 +- .../person/HsOfficePersonEntityUnitTest.java | 12 +- ...OfficePersonRepositoryIntegrationTest.java | 8 +- ...RelationshipRepositoryIntegrationTest.java | 8 +- ...ceSepaMandateControllerAcceptanceTest.java | 4 +- 24 files changed, 207 insertions(+), 140 deletions(-) create mode 100644 src/main/resources/db/changelog/009-check-environment.sql delete mode 100644 src/main/resources/db/changelog/009-environment-defaults.sql diff --git a/.aliases b/.aliases index 455bcd79..bd563eb2 100644 --- a/.aliases +++ b/.aliases @@ -74,3 +74,5 @@ alias pg-sql-restore='gunzip --stdout | docker exec -i hsadmin-ng-postgres psql alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l' +alias spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' + diff --git a/build.gradle b/build.gradle index dff765fc..20ad0442 100644 --- a/build.gradle +++ b/build.gradle @@ -100,6 +100,7 @@ dependencies { testImplementation 'io.rest-assured:spring-mock-mvc' testImplementation 'org.hamcrest:hamcrest-core:2.2' testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1' + testImplementation 'org.junit.jupiter:junit-jupiter-api' } dependencyManagement { @@ -237,12 +238,9 @@ test { excludes = [ 'net.hostsharing.hsadminng.**.generated.**', ] - - test { - systemProperty "my_variable", System.getenv("MY_VARIABLE") + useJUnitPlatform { + excludeTags 'import' } - - } jacocoTestReport { dependsOn test @@ -303,6 +301,17 @@ jacocoTestCoverageVerification { } } +tasks.register('importTest', Test) { + useJUnitPlatform { + includeTags 'import' + } + + group 'verification' + description 'run the import jobs as tests' +} +test.dependsOn tasks.importTest + + // pitest mutation testing pitest { targetClasses = ['net.hostsharing.hsadminng.**'] diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md index bc7b04e5..db5ee06e 100644 --- a/doc/hs-office-data-structure.md +++ b/doc/hs-office-data-structure.md @@ -1,25 +1,28 @@ +# Beispiel: juristische Person (GmbH) + ```mermaid classDiagram - direction RL + direction TD - namespace Geschäftspartner { + namespace Geschäftspartner { + class partner-MeierGmbH - class partnerDetails-MeierGmbH class contact-MeierGmbH class person-MeierGmbH - class debitor-MeierGmbH - class contact-MeierGmbH-Buha - class person-FrankMeier class contact-FrankMeier - class rel-MeierGmbH-FrankMeier + class role-MeierGmbH-FrankMeier class person-SabineMeier class contact-SabineMeier - class rel-MeierGmbH-SabineMeier - } - + class role-MeierGmbH-SabineMeier + + class debitor-MeierGmbH + class contact-MeierGmbH-Buha + class role-MeierGmbH-Buha + } + class partner-MeierGmbH { +person +contact @@ -27,20 +30,17 @@ classDiagram } partner-MeierGmbH o-- person-MeierGmbH partner-MeierGmbH o-- contact-MeierGmbH - partner-MeierGmbH *-- partnerDetails-MeierGmbH - - class partnerDetails-MeierGmbH { - +registrationOffice - +registrationNumber - +birthName - +dateOfDeath - } class person-MeierGmbH { - + personType: LEGAL - + tradeName: Meier GmbH - + familyName - + givenName + +personType: LEGAL + +tradeName: Meier GmbH + +familyName + +givenName + +registrationOffice: AG Hamburg + +registrationNumber: ABC123434 + +birthName + +birthPlace + +dateOfDeath } class contact-MeierGmbH { @@ -51,8 +51,8 @@ classDiagram class debitor-MeierGmbH { +Partner partner - +Numeric(2) debitorNumberSuffix: 00 - +Contact billingContact + +Numeric[2] debitorNumberSuffix: 00 + +Role billingRole +boolean billable: true +String vatId: ID123456789 +String vatCountryCode: DE @@ -62,14 +62,24 @@ classDiagram +String defaultPrefix: mei } debitor-MeierGmbH o-- partner-MeierGmbH - debitor-MeierGmbH o-- contact-MeierGmbH-Buha - + debitor-MeierGmbH o-- role-MeierGmbH-Buha + class contact-MeierGmbH-Buha { +postalAddress: Hauptstraße 5, 22345 Hamburg +phoneNumbers: +49 40 12345-05 +emailAddresses: buha@meier-gmbh.de } + class role-MeierGmbH-Buha { + +RelType relType ACCOUNTING + +Person relAnchor + +Person relHolder + +Contact contact + } + role-MeierGmbH-Buha o-- person-MeierGmbH : relAnchor + role-MeierGmbH-Buha o-- person-MeierGmbH : relHolder + role-MeierGmbH-Buha o-- contact-MeierGmbH-Buha + class person-FrankMeier { + personType: NATURAL + tradeName @@ -80,18 +90,18 @@ classDiagram class contact-FrankMeier { +postalAddress +phoneNumbers: +49 40 12345-22 - +emailAddresses: sabine.meier@meier-gmbh.de + +emailAddresses: frank.meier@meier-gmbh.de } - class rel-MeierGmbH-FrankMeier { + class role-MeierGmbH-FrankMeier { +RelType relType REPRESENTATIVE +Person relAnchor +Person relHolder +Contact contact } - rel-MeierGmbH-FrankMeier o-- person-MeierGmbH : relAnchor - rel-MeierGmbH-FrankMeier o-- person-FrankMeier : relHolder - rel-MeierGmbH-FrankMeier o-- contact-FrankMeier + role-MeierGmbH-FrankMeier o-- person-MeierGmbH : relAnchor + role-MeierGmbH-FrankMeier o-- person-FrankMeier : relHolder + role-MeierGmbH-FrankMeier o-- contact-FrankMeier class person-SabineMeier { +personType: NATURAL @@ -106,15 +116,15 @@ classDiagram +emailAddresses: sabine.meier@meier-gmbh.de } - class rel-MeierGmbH-SabineMeier { + class role-MeierGmbH-SabineMeier { +RelType relType OPERATIONAL +Person relAnchor +Person relHolder +Contact contact } - rel-MeierGmbH-SabineMeier o-- person-MeierGmbH : relAnchor - rel-MeierGmbH-SabineMeier o-- person-SabineMeier : relHolder - rel-MeierGmbH-SabineMeier o-- contact-SabineMeier + role-MeierGmbH-SabineMeier o-- person-MeierGmbH : relAnchor + role-MeierGmbH-SabineMeier o-- person-SabineMeier : relHolder + role-MeierGmbH-SabineMeier o-- contact-SabineMeier namespace Enums { @@ -128,24 +138,13 @@ classDiagram class PersonType { <> - UNKNOWN - NATURAL - LEGAL - SOLE_REPRESENTATION - JOINT_REPRESENTATION + UNKNOWN: nur für Import + NATURAL_PERSON: natürliche Person + LEGAL_PERSON: z.B. GmbH, e.K., eG, e.V. + INCORORATED_FIRM: z.B. OHG, Partnerschaftsgesellschaft + UNINCORPORATED_FIRM: z.B. GbR, ARGE, Erbengemeinschaft + PUBLIC_INSTITUTION: KdöR, AöR [ohne Registergericht/Registernummer] } - } -``` -```mermaid -classDiagram - classA --|> classB : Inheritance - classC --* classD : Composition - classE --o classF : Aggregation - classG --> classH : Association - classI -- classJ : Link(Solid) - classK ..> classL : Dependency - classM ..|> classN : Realization - classO .. classP : Link(Dashed) ``` 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 f6c46772..6f0ee297 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 @@ -51,7 +51,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { @ManyToOne @JoinColumn(name = "billingcontactuuid") - private HsOfficeContactEntity billingContact; + private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson @Column(name = "billable") private boolean billable; 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 d7c50779..ea09eb44 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 @@ -27,8 +27,9 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable { "partnerDetails") .withProp(HsOfficePartnerDetailsEntity::getRegistrationOffice) .withProp(HsOfficePartnerDetailsEntity::getRegistrationNumber) + .withProp(HsOfficePartnerDetailsEntity::getBirthPlace) .withProp(HsOfficePartnerDetailsEntity::getBirthday) - .withProp(HsOfficePartnerDetailsEntity::getBirthday) + .withProp(HsOfficePartnerDetailsEntity::getBirthName) .withProp(HsOfficePartnerDetailsEntity::getDateOfDeath) .withSeparator(", ") .quotedValues(false); @@ -40,6 +41,7 @@ public class HsOfficePartnerDetailsEntity implements HasUuid, Stringifyable { private @Column(name = "registrationoffice") String registrationOffice; private @Column(name = "registrationnumber") String registrationNumber; private @Column(name = "birthname") String birthName; + private @Column(name = "birthplace") String birthPlace; private @Column(name = "birthday") LocalDate birthday; private @Column(name = "dateofdeath") LocalDate dateOfDeath; 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 932c92a4..94a1a74e 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 @@ -54,6 +54,7 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return !StringUtils.isEmpty(tradeName) ? tradeName : (familyName + ", " + givenName); + return personType + " " + + (!StringUtils.isEmpty(tradeName) ? tradeName : (familyName + ", " + givenName)); } } diff --git a/src/main/resources/db/changelog/009-check-environment.sql b/src/main/resources/db/changelog/009-check-environment.sql new file mode 100644 index 00000000..0bad2670 --- /dev/null +++ b/src/main/resources/db/changelog/009-check-environment.sql @@ -0,0 +1,18 @@ +--liquibase formatted sql + + +-- ============================================================================ +-- NUMERIC-HASH-FUNCTIONS +--changeset hash:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +do $$ + begin + if starts_with ('${HSADMINNG_POSTGRES_ADMIN_USERNAME}', '$') then + RAISE EXCEPTION 'environment variable HSADMINNG_POSTGRES_ADMIN_USERNAME not set'; + end if; + if starts_with ('${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}', '$') then + RAISE EXCEPTION 'environment variable HSADMINNG_POSTGRES_RESTRICTED_USERNAME not set'; + end if; + end $$ +--// diff --git a/src/main/resources/db/changelog/009-environment-defaults.sql b/src/main/resources/db/changelog/009-environment-defaults.sql deleted file mode 100644 index fa69a98b..00000000 --- a/src/main/resources/db/changelog/009-environment-defaults.sql +++ /dev/null @@ -1,12 +0,0 @@ ---liquibase formatted sql - - --- ============================================================================ --- NUMERIC-HASH-FUNCTIONS ---changeset hash:1 endDelimiter:--// --- ---------------------------------------------------------------------------- - -create function postgresAdminUserName() returns text as $$ -if -$$ language sql; ---// diff --git a/src/main/resources/db/changelog/220-hs-office-partner.sql b/src/main/resources/db/changelog/220-hs-office-partner.sql index 689bb1f0..340c0455 100644 --- a/src/main/resources/db/changelog/220-hs-office-partner.sql +++ b/src/main/resources/db/changelog/220-hs-office-partner.sql @@ -10,6 +10,7 @@ create table hs_office_partner_details uuid uuid unique references RbacObject (uuid) initially deferred, registrationOffice varchar(96), registrationNumber varchar(96), + birthPlace varchar(96), birthName varchar(96), birthday date, dateOfDeath date diff --git a/src/main/resources/db/changelog/224-hs-office-partner-details-rbac.sql b/src/main/resources/db/changelog/224-hs-office-partner-details-rbac.sql index 97bab5e5..ab94481e 100644 --- a/src/main/resources/db/changelog/224-hs-office-partner-details-rbac.sql +++ b/src/main/resources/db/changelog/224-hs-office-partner-details-rbac.sql @@ -29,6 +29,7 @@ call generateRbacRestrictedView('hs_office_partner_details', $updates$ registrationOffice = new.registrationOffice, registrationNumber = new.registrationNumber, + birthPlace = new.birthPlace, birthName = new.birthName, birthday = new.birthday, dateOfDeath = new.dateOfDeath diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 68719b66..88c70bef 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -11,6 +11,8 @@ databaseChangeLog: file: db/changelog/005-uuid-ossp-extension.sql - include: file: db/changelog/006-numeric-hash-functions.sql + - include: + file: db/changelog/009-check-environment.sql - include: file: db/changelog/010-context.sql - include: diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java index 961305f9..270431b9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java @@ -4,6 +4,7 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; 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 org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -16,6 +17,7 @@ class HsOfficeDebitorEntityUnitTest { .debitorNumberSuffix((byte)67) .partner(HsOfficePartnerEntity.builder() .person(HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) .tradeName("some trade name") .build()) .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) @@ -27,7 +29,7 @@ class HsOfficeDebitorEntityUnitTest { final var result = given.toString(); - assertThat(result).isEqualTo("debitor(1234567: some trade name: som)"); + assertThat(result).isEqualTo("debitor(1234567: LEGAL some trade name: som)"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index f6dcc838..b6fb9332 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -215,9 +215,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then allTheseDebitorsAreReturned( result, - "debitor(1000111: First GmbH: fir)", - "debitor(1000212: Second e.K.: sec)", - "debitor(1000313: Third OHG: thi)"); + "debitor(1000111: LEGAL First GmbH: fir)", + "debitor(1000212: LEGAL Second e.K.: sec)", + "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); } @ParameterizedTest @@ -235,8 +235,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then: exactlyTheseDebitorsAreReturned(result, - "debitor(1000111: First GmbH: fir)", - "debitor(1000120: First GmbH: fif)"); + "debitor(1000111: LEGAL First GmbH: fir)", + "debitor(1000120: LEGAL First GmbH: fif)"); } @Test @@ -264,7 +264,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByDebitorNumber(1000313); // then - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG: thi)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); } } @@ -280,7 +280,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); // then - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG: thi)"); + exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index c13a85b6..199d09f6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -287,7 +287,7 @@ class HsOfficeMembershipControllerAcceptanceTest { // finally, the Membership is actually updated assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getPartner().toShortString()).isEqualTo("First GmbH"); + assertThat(mandate.getPartner().toShortString()).isEqualTo("LEGAL First GmbH"); assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString()); assertThat(mandate.getMemberNumber()).isEqualTo(givenMembership.getMemberNumber()); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)"); @@ -330,7 +330,7 @@ class HsOfficeMembershipControllerAcceptanceTest { // finally, the Membership is actually updated assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getPartner().toShortString()).isEqualTo("First GmbH"); + assertThat(mandate.getPartner().toShortString()).isEqualTo("LEGAL First GmbH"); assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString()); assertThat(mandate.getMemberNumber()).isEqualTo(givenMembership.getMemberNumber()); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index cae439f2..14959428 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -27,8 +27,7 @@ class HsOfficeMembershipEntityUnitTest { void toStringContainsAllProps() { final var result = givenMembership.toString(); - assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))"); -// assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))"); + assertThat(result).isEqualTo("Membership(10001, LEGAL Test Ltd., 1000100, [2020-01-01,))"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 91555669..50f18b64 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -185,9 +185,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // then exactlyTheseMembershipsAreReturned( result, - "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)", - "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)", - "Membership(10003, Third OHG, 1000313, [2022-10-01,), NONE)"); + "Membership(10001, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)", + "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)", + "Membership(10003, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)"); } @Test @@ -202,7 +202,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { null); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, "Membership(10001, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); } @Test @@ -214,7 +214,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); } } 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 bcbee321..aec13e8e 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 @@ -23,6 +23,10 @@ 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.*; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.TestWatcher; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -48,6 +52,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.Assumptions.assumeThat; import static org.assertj.core.api.Fail.fail; /* @@ -91,7 +96,7 @@ import static org.assertj.core.api.Fail.fail; * * import-office-tables # comes from .aliases file */ -//@Disabled +@Tag("import") @DataJpaTest(properties = { "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:admin}", @@ -99,6 +104,7 @@ import static org.assertj.core.api.Fail.fail; }) @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@ExtendWith(OrderedDependedTestsExtension.class) public class ImportOfficeTables extends ContextBasedTest { @Value("${spring.datasource.username}") @@ -151,23 +157,23 @@ public class ImportOfficeTables extends ContextBasedTest { // no contacts yet => mostly null values assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { - 7=partner(null, null), - 10=partner(null, null), - 12=partner(null, null) + 7=partner(UNKNOWN null, null), + 10=partner(UNKNOWN null, null), + 12=partner(UNKNOWN null, null) } """); assertThat(contacts.toString()).isEqualTo("{}"); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(1000700: null, null: mih), - 10=debitor(1001000: null, null: xyz), - 12=debitor(1101200: null, null: xxx)} + 7=debitor(1000700: UNKNOWN null, null: mih), + 10=debitor(1001000: UNKNOWN null, null: xyz), + 12=debitor(1101200: UNKNOWN null, null: xxx)} """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, null, null, 1000700, [2000-12-06,), NONE), - 10=Membership(10010, null, null, 1001000, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, null, null, 1101200, [2021-04-01,), NONE) + 7=Membership(10007, UNKNOWN null, null, 1000700, [2000-12-06,), NONE), + 10=Membership(10010, UNKNOWN null, null, 1001000, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, UNKNOWN null, null, 1101200, [2021-04-01,), NONE) } """); } @@ -189,9 +195,9 @@ public class ImportOfficeTables extends ContextBasedTest { assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" { - 7=partner(Mellies, Michael: Herr Michael Mellies ), - 10=partner(JM e.K.: Herr Philip Meyer-Contract , JM e.K.), - 12=partner(Test PS: Petra Schmidt , Test PS) + 7=partner(NATURAL Mellies, Michael: Herr Michael Mellies ), + 10=partner(LEGAL JM e.K.: Herr Philip Meyer-Contract , JM e.K.), + 12=partner(LEGAL Test PS: Petra Schmidt , Test PS) } """); assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" @@ -205,31 +211,31 @@ public class ImportOfficeTables extends ContextBasedTest { """); assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" { - 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') + 900000=person(personType='NATURAL', tradeName='', familyName='Mellies', givenName='Michael'), + 900001=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Contract', givenName='Philip'), + 900002=person(personType='LEGAL', tradeName='Test PS', familyName='Schmidt', givenName='Petra') } """); assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" { - 7=debitor(1000700: Mellies, Michael: mih), - 10=debitor(1001000: JM e.K.: xyz), - 12=debitor(1101200: Test PS: xxx) + 7=debitor(1000700: NATURAL Mellies, Michael: mih), + 10=debitor(1001000: LEGAL JM e.K.: xyz), + 12=debitor(1101200: LEGAL Test PS: xxx) } """); assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, Mellies, Michael, 1000700, [2000-12-06,), NONE), - 10=Membership(10010, JM e.K., 1001000, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, Test PS, 1101200, [2021-04-01,), NONE) + 7=Membership(10007, NATURAL Mellies, Michael, 1000700, [2000-12-06,), NONE), + 10=Membership(10010, LEGAL JM e.K., 1001000, [2000-12-06,2016-01-01), UNKNOWN), + 12=Membership(11012, LEGAL Test PS, 1101200, [2021-04-01,), NONE) } """); assertThat(relationships.toString()).isEqualToIgnoringWhitespace(""" { - 1101=rel(relAnchor='Mellies, Michael', relType='OPERATIONS', relHolder='Mellies, Michael', contact='Herr Michael Mellies '), - 1202=rel(relAnchor='JM e.K.', relType='OPERATIONS', relHolder='JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), - 1203=rel(relAnchor='JM e.K.', relType='REPRESENTATIVE', relHolder='JM e.K.', contact='Herr Philip Meyer-Contract , JM e.K.'), - 1301=rel(relAnchor='Test PS', relType='REPRESENTATIVE', relHolder='Test PS', contact='Petra Schmidt , Test PS') + 1101=rel(relAnchor='NATURAL Mellies, Michael', relType='OPERATIONS', relHolder='NATURAL Mellies, Michael', contact='Herr Michael Mellies '), + 1202=rel(relAnchor='LEGAL JM e.K.', relType='OPERATIONS', relHolder='LEGAL JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), + 1203=rel(relAnchor='LEGAL JM e.K.', relType='REPRESENTATIVE', relHolder='LEGAL JM e.K.', contact='Herr Philip Meyer-Contract , JM e.K.'), + 1301=rel(relAnchor='LEGAL Test PS', relType='REPRESENTATIVE', relHolder='LEGAL Test PS', contact='Petra Schmidt , Test PS') } """); } @@ -520,7 +526,6 @@ public class ImportOfficeTables extends ContextBasedTest { final var person = HsOfficePersonEntity.builder() .personType(HsOfficePersonType.UNKNOWN) // TODO .build(); -// persons.put(rec.getInteger("bp_id"), person); persons.put(personId++, person); final var partner = HsOfficePartnerEntity.builder() @@ -684,6 +689,7 @@ public class ImportOfficeTables extends ContextBasedTest { final var person = partner.getPerson(); person.setTradeName(rec.getString("firma")); + determinePersonType(person); // TODO: title+salutation: add to person person.setGivenName(rec.getString("first_name")); person.setFamilyName(rec.getString("last_name")); @@ -734,6 +740,14 @@ public class ImportOfficeTables extends ContextBasedTest { }); } + private void determinePersonType(final HsOfficePersonEntity person) { + if (person.getTradeName().isBlank()) { + person.setPersonType(HsOfficePersonType.NATURAL); + } else { + person.setPersonType(HsOfficePersonType.LEGAL); // TODO: add rules if we distinguish + } + } + private HsOfficeContactEntity initContact(final HsOfficeContactEntity contact, final Record rec) { contacts.put(rec.getInteger("contact_id"), contact); @@ -916,3 +930,20 @@ class Record { return null; } } + +class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { + private static boolean previousTestsPassed = true; + + public void testAborted(ExtensionContext context, Throwable cause) { + previousTestsPassed = false; + } + + public void testFailed(ExtensionContext context, Throwable cause) { + previousTestsPassed = false; + } + + @Override + public void beforeEach(final ExtensionContext extensionContext) throws Exception { + assumeThat(previousTestsPassed).isTrue(); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityUnitTest.java index 24228031..5a926f0c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityUnitTest.java @@ -21,7 +21,8 @@ class HsOfficePartnerDetailsEntityUnitTest { final var result = given.toString(); - assertThat(result).isEqualTo("partnerDetails(Hamburg, 12345, 2002-01-15, 2002-01-15, 2081-12-21)"); + assertThat(result).isEqualTo( + "partnerDetails(Hamburg, 12345, 2002-01-15, Melly Miller, 2081-12-21)"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java index a0ad4c46..87aeea12 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntityUnitTest.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.partner; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -11,24 +12,30 @@ class HsOfficePartnerEntityUnitTest { @Test void toStringContainsPersonAndContact() { final var given = HsOfficePartnerEntity.builder() - .person(HsOfficePersonEntity.builder().tradeName("some trade name").build()) + .person(HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) + .tradeName("some trade name") + .build()) .contact(HsOfficeContactEntity.builder().label("some label").build()) .build(); final var result = given.toString(); - assertThat(result).isEqualTo("partner(some trade name: some label)"); + assertThat(result).isEqualTo("partner(LEGAL some trade name: some label)"); } @Test void toShortStringContainsPersonAndContact() { final var given = HsOfficePartnerEntity.builder() - .person(HsOfficePersonEntity.builder().tradeName("some trade name").build()) + .person(HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) + .tradeName("some trade name") + .build()) .contact(HsOfficeContactEntity.builder().label("some label").build()) .build(); final var result = given.toShortString(); - assertThat(result).isEqualTo("some trade name"); + assertThat(result).isEqualTo("LEGAL some trade name"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index 7002685d..ad41e939 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -177,9 +177,9 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { // then allThesePartnersAreReturned( result, - "partner(Third OHG: third contact)", - "partner(Second e.K.: second contact)", - "partner(First GmbH: first contact)"); + "partner(SOLE_REPRESENTATION Third OHG: third contact)", + "partner(LEGAL Second e.K.: second contact)", + "partner(LEGAL First GmbH: first contact)"); } @Test @@ -191,7 +191,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { final var result = partnerRepo.findPartnerByOptionalNameLike(null); // then: - exactlyThesePartnersAreReturned(result, "partner(First GmbH: first contact)"); + exactlyThesePartnersAreReturned(result, "partner(LEGAL First GmbH: first contact)"); } } @@ -207,7 +207,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { final var result = partnerRepo.findPartnerByOptionalNameLike("third contact"); // then - exactlyThesePartnersAreReturned(result, "partner(Third OHG: third contact)"); + exactlyThesePartnersAreReturned(result, "partner(SOLE_REPRESENTATION Third OHG: third contact)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntityUnitTest.java index 5b7ca93d..9502bfb3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonEntityUnitTest.java @@ -11,29 +11,32 @@ class HsOfficePersonEntityUnitTest { @Test void getDisplayReturnsTradeNameIfAvailable() { final var givenPersonEntity = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) .tradeName("some trade name") .build(); final var actualDisplay = givenPersonEntity.toShortString(); - assertThat(actualDisplay).isEqualTo("some trade name"); + assertThat(actualDisplay).isEqualTo("LEGAL some trade name"); } @Test void getDisplayReturnsFamilyAndGivenNameIfNoTradeNameAvailable() { final var givenPersonEntity = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.NATURAL) .familyName("some family name") .givenName("some given name") .build(); final var actualDisplay = givenPersonEntity.toShortString(); - assertThat(actualDisplay).isEqualTo("some family name, some given name"); + assertThat(actualDisplay).isEqualTo("NATURAL some family name, some given name"); } @Test void toShortStringWithTradeNameReturnsTradeName() { final var givenPersonEntity = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) .tradeName("some trade name") .familyName("some family name") .givenName("some given name") @@ -41,19 +44,20 @@ class HsOfficePersonEntityUnitTest { final var actualDisplay = givenPersonEntity.toShortString(); - assertThat(actualDisplay).isEqualTo("some trade name"); + assertThat(actualDisplay).isEqualTo("LEGAL some trade name"); } @Test void toShortStringWithoutTradeNameReturnsFamilyAndGivenName() { final var givenPersonEntity = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.NATURAL) .familyName("some family name") .givenName("some given name") .build(); final var actualDisplay = givenPersonEntity.toShortString(); - assertThat(actualDisplay).isEqualTo("some family name, some given name"); + assertThat(actualDisplay).isEqualTo("NATURAL some family name, some given name"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java index 2405b237..59243daf 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonRepositoryIntegrationTest.java @@ -144,10 +144,10 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTest { // then allThesePersonsAreReturned( result, - "Smith, Peter", - "Second e.K.", - "Third OHG", - "Erben Bessler"); + "NATURAL Smith, Peter", + "LEGAL Second e.K.", + "SOLE_REPRESENTATION Third OHG", + "JOINT_REPRESENTATION Erben Bessler"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java index 326227ff..7b2603f8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipRepositoryIntegrationTest.java @@ -159,9 +159,9 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { // then allTheseRelationshipsAreReturned( result, - "rel(relAnchor='First GmbH', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='first contact')", - "rel(relAnchor='Third OHG', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='third contact')", - "rel(relAnchor='Second e.K.', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='second contact')"); + "rel(relAnchor='LEGAL First GmbH', relType='REPRESENTATIVE', relHolder='NATURAL Smith, Peter', contact='first contact')", + "rel(relAnchor='SOLE_REPRESENTATION Third OHG', relType='REPRESENTATIVE', relHolder='NATURAL Smith, Peter', contact='third contact')", + "rel(relAnchor='LEGAL Second e.K.', relType='REPRESENTATIVE', relHolder='NATURAL Smith, Peter', contact='second contact')"); } @Test @@ -176,7 +176,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest { // then: exactlyTheseRelationshipsAreReturned( result, - "rel(relAnchor='First GmbH', relType='REPRESENTATIVE', relHolder='Smith, Peter', contact='first contact')"); + "rel(relAnchor='LEGAL First GmbH', relType='REPRESENTATIVE', relHolder='NATURAL Smith, Peter', contact='first contact')"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index f84525a6..70cf53da 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH: fir)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); @@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { // finally, the sepaMandate is actually updated assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH: fir)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); -- 2.39.2 From d03fb7046561d58d59e112665093322ea2eabf86 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 18 Jan 2024 13:57:46 +0100 Subject: [PATCH 45/72] UML object diagram for new target data model for business partners --- doc/hs-office-data-structure.md | 75 ++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md index db5ee06e..e5d83316 100644 --- a/doc/hs-office-data-structure.md +++ b/doc/hs-office-data-structure.md @@ -4,46 +4,51 @@ classDiagram direction TD - namespace Geschäftspartner { + namespace Partner { class partner-MeierGmbH - class contact-MeierGmbH + class personDetails-MeierGmbH + class contactData-MeierGmbH class person-MeierGmbH class person-FrankMeier - class contact-FrankMeier + class contactData-FrankMeier class role-MeierGmbH-FrankMeier class person-SabineMeier - class contact-SabineMeier + class contactData-SabineMeier class role-MeierGmbH-SabineMeier class debitor-MeierGmbH - class contact-MeierGmbH-Buha + class contactData-MeierGmbH-Buha class role-MeierGmbH-Buha } class partner-MeierGmbH { + +partnerNumber: 12345 +person - +contact - +details } partner-MeierGmbH o-- person-MeierGmbH - partner-MeierGmbH o-- contact-MeierGmbH class person-MeierGmbH { +personType: LEGAL +tradeName: Meier GmbH +familyName +givenName + } + person-MeierGmbH *-- personDetails-MeierGmbH : optiona + + class personDetails-MeierGmbH { + +personContact +registrationOffice: AG Hamburg +registrationNumber: ABC123434 +birthName +birthPlace +dateOfDeath } + personDetails-MeierGmbH o-- contactData-MeierGmbH - class contact-MeierGmbH { + class contactData-MeierGmbH { +postalAddress: Hauptstraße 5, 22345 Hamburg +phoneNumbers: +49 40 12345-00 +emailAddresses: office@meier-gmbh.de @@ -62,23 +67,23 @@ classDiagram +String defaultPrefix: mei } debitor-MeierGmbH o-- partner-MeierGmbH - debitor-MeierGmbH o-- role-MeierGmbH-Buha + debitor-MeierGmbH *-- role-MeierGmbH-Buha - class contact-MeierGmbH-Buha { + class contactData-MeierGmbH-Buha { +postalAddress: Hauptstraße 5, 22345 Hamburg +phoneNumbers: +49 40 12345-05 +emailAddresses: buha@meier-gmbh.de } class role-MeierGmbH-Buha { - +RelType relType ACCOUNTING - +Person relAnchor - +Person relHolder - +Contact contact + +RoleType RoleType ACCOUNTING + +Person anchor + +Person holder + +Contact roleContact } - role-MeierGmbH-Buha o-- person-MeierGmbH : relAnchor - role-MeierGmbH-Buha o-- person-MeierGmbH : relHolder - role-MeierGmbH-Buha o-- contact-MeierGmbH-Buha + role-MeierGmbH-Buha o-- person-MeierGmbH : anchor + role-MeierGmbH-Buha o-- person-MeierGmbH : holder + role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha class person-FrankMeier { + personType: NATURAL @@ -87,21 +92,21 @@ classDiagram + givenName: Frank } - class contact-FrankMeier { + class contactData-FrankMeier { +postalAddress +phoneNumbers: +49 40 12345-22 +emailAddresses: frank.meier@meier-gmbh.de } class role-MeierGmbH-FrankMeier { - +RelType relType REPRESENTATIVE - +Person relAnchor - +Person relHolder - +Contact contact + +RoleType RoleType REPRESENTATIVE + +Person anchor + +Person holder + +Contact roleContact } - role-MeierGmbH-FrankMeier o-- person-MeierGmbH : relAnchor - role-MeierGmbH-FrankMeier o-- person-FrankMeier : relHolder - role-MeierGmbH-FrankMeier o-- contact-FrankMeier + role-MeierGmbH-FrankMeier o-- person-MeierGmbH : anchor + role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder + role-MeierGmbH-FrankMeier o-- contactData-FrankMeier class person-SabineMeier { +personType: NATURAL @@ -110,25 +115,25 @@ classDiagram +givenName: Sabine } - class contact-SabineMeier { + class contactData-SabineMeier { +postalAddress +phoneNumbers: +49 40 12345-22 +emailAddresses: sabine.meier@meier-gmbh.de } class role-MeierGmbH-SabineMeier { - +RelType relType OPERATIONAL - +Person relAnchor - +Person relHolder - +Contact contact + +RoleType RoleType OPERATIONAL + +Person anchor + +Person holder + +Contact roleContact } - role-MeierGmbH-SabineMeier o-- person-MeierGmbH : relAnchor - role-MeierGmbH-SabineMeier o-- person-SabineMeier : relHolder - role-MeierGmbH-SabineMeier o-- contact-SabineMeier + role-MeierGmbH-SabineMeier o-- person-MeierGmbH : anchor + role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder + role-MeierGmbH-SabineMeier o-- contactData-SabineMeier namespace Enums { - class RelType { + class RoleType { <> UNKNOWN REPRESENTATIVE -- 2.39.2 From 007e1522b77aabf9b53bc9b3adf687b45199d22d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 18 Jan 2024 14:03:06 +0100 Subject: [PATCH 46/72] add ex-partner to Relationship-Type and properly extract distinct persons from contacts.csv --- .../membership/HsOfficeMembershipEntity.java | 2 +- .../HsOfficeRelationshipType.java | 1 + .../changelog/213-hs-office-person-rbac.sql | 1 + .../changelog/230-hs-office-relationship.sql | 2 +- .../office/migration/ImportOfficeTables.java | 381 ++++++++++-------- .../migration/asset-transactions.csv | 16 +- .../resources/migration/business-partners.csv | 6 +- src/test/resources/migration/contacts.csv | 10 +- .../resources/migration/sepa-mandates.csv | 4 +- .../migration/share-transactions.csv | 8 +- 10 files changed, 242 insertions(+), 189 deletions(-) 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 9f8cb688..14528c2f 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 @@ -53,7 +53,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { private HsOfficeDebitorEntity mainDebitor; @Column(name = "membernumber") - private int memberNumber; + private int memberNumber; // TODO: migrate to suffix, like debitorNumberSuffix @Column(name = "validity", columnDefinition = "daterange") @Type(PostgreSQLRangeType.class) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java index 0535ca67..6d4efc84 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.relationship; public enum HsOfficeRelationshipType { UNKNOWN, + EX_PARTNER, REPRESENTATIVE, ACCOUNTING, OPERATIONS diff --git a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql index 4f217e7a..42eacf2f 100644 --- a/src/main/resources/db/changelog/213-hs-office-person-rbac.sql +++ b/src/main/resources/db/changelog/213-hs-office-person-rbac.sql @@ -37,6 +37,7 @@ begin grantedByRole => globalAdmin() ); + -- TODO: who is admin? the person itself? is it allowed for the person itself or a representative to edit the data? perform createRoleWithGrants( hsOfficePersonAdmin(NEW), permissions => array['edit'], diff --git a/src/main/resources/db/changelog/230-hs-office-relationship.sql b/src/main/resources/db/changelog/230-hs-office-relationship.sql index 826b7bba..d1d4a366 100644 --- a/src/main/resources/db/changelog/230-hs-office-relationship.sql +++ b/src/main/resources/db/changelog/230-hs-office-relationship.sql @@ -4,7 +4,7 @@ --changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'REPRESENTATIVE', 'ACCOUNTING', 'OPERATIONS'); +CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'EX-PARTNER', 'REPRESENTATIVE', 'ACCOUNTING', 'OPERATIONS'); CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT; 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 aec13e8e..2c2d6707 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 @@ -22,11 +22,12 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.test.JpaAttempt; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.BeforeEachCallback; -import org.junit.jupiter.api.extension.TestWatcher; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestWatcher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -45,6 +46,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDate; import java.util.*; +import java.util.stream.Collectors; import static java.util.Arrays.stream; import static java.util.Objects.requireNonNull; @@ -85,22 +87,24 @@ import static org.assertj.core.api.Fail.fail; -- maybe something like that is needed for the 2nd user -- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to hsh99_restricted; - * Then copy this to a file named .environment (excluded from git): + * Then copy this to a file named .environment (excluded from git) and fill in your specific values: export HSADMINNG_POSTGRES_JDBC_URL=jdbc:postgresql://localhost:6432/hsh99_hsadminng export HSADMINNG_POSTGRES_ADMIN_USERNAME=hsh99_admin export HSADMINNG_POSTGRES_ADMIN_PASSWORD=password export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted + export HSADMINNG_SUPERUSER=some-precreated-superuser@example.org - * Then, import the office data, uncomment the @Diabled and then run: + * To finally import the office data, run: * - * import-office-tables # comes from .aliases file + * import-office-tables # comes from .aliases file and uses .environment */ @Tag("import") @DataJpaTest(properties = { "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:admin}", - "spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}" + "spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}", + "hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}" }) @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @@ -110,8 +114,8 @@ public class ImportOfficeTables extends ContextBasedTest { @Value("${spring.datasource.username}") private String postgresAdminUser; - // TODO: use real rbacSuperuser for actual import - private static final String rbacSuperuser = "superuser-alex@hostsharing.net"; + @Value("${hsadminng.superuser}") + private String rbacSuperuser; private static NavigableMap contacts = new TreeMap<>(); private static NavigableMap persons = new TreeMap<>(); @@ -150,32 +154,30 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(postgresAdminUser) ) { - return; - } + assumeThat(postgresAdminUser).isEqualTo("admin"); // no contacts yet => mostly null values - assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" - { - 7=partner(UNKNOWN null, null), - 10=partner(UNKNOWN null, null), - 12=partner(UNKNOWN null, null) - } - """); - assertThat(contacts.toString()).isEqualTo("{}"); - assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" { - 7=debitor(1000700: UNKNOWN null, null: mih), - 10=debitor(1001000: UNKNOWN null, null: xyz), - 12=debitor(1101200: UNKNOWN null, null: xxx)} + 17=partner(null null, null), + 20=partner(null null, null), + 22=partner(null null, null) + } + """); + assertThat(toFormattedString(contacts)).isEqualTo("{}"); + assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" + { + 17=debitor(1001700: null null, null: mih), + 20=debitor(1002000: null null, null: xyz), + 22=debitor(1102200: null null, null: xxx)} + """); + assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" + { + 17=Membership(10017, null null, null, 1001700, [2000-12-06,), NONE), + 20=Membership(10020, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), + 22=Membership(11022, null null, null, 1102200, [2021-04-01,), NONE) + } """); - assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" - { - 7=Membership(10007, UNKNOWN null, null, 1000700, [2000-12-06,), NONE), - 10=Membership(10010, UNKNOWN null, null, 1001000, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, UNKNOWN null, null, 1101200, [2021-04-01,), NONE) - } - """); } @Test @@ -189,48 +191,48 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(postgresAdminUser) ) { - return; - } + assumeThat(postgresAdminUser).isEqualTo("admin"); - assertThat(partners.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" { - 7=partner(NATURAL Mellies, Michael: Herr Michael Mellies ), - 10=partner(LEGAL JM e.K.: Herr Philip Meyer-Contract , JM e.K.), - 12=partner(LEGAL Test PS: Petra Schmidt , Test PS) + 17=partner(NATURAL Mellies, Michael: Herr Michael Mellies ), + 20=partner(LEGAL JM e.K.: Herr Philip Meyer-Contract , JM e.K.), + 22=partner(LEGAL Test PS: Petra Schmidt , Test PS) } """); - assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace(""" { - 1101=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), - 1201=contact(label='Frau Dr. Jenny Meyer-Billing , JM e.K.', emailAddresses='jm-billing@example.org'), - 1202=contact(label='Herr Andrew Meyer-Operation , JM e.K.', emailAddresses='am-operation@example.org'), - 1203=contact(label='Herr Philip Meyer-Contract , JM e.K.', emailAddresses='pm-partner@example.org'), - 1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') + 1101=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), + 1201=contact(label='Frau Dr. Jenny Meyer-Billing , JM e.K.', emailAddresses='jm-billing@example.org'), + 1202=contact(label='Herr Andrew Meyer-Operation , JM e.K.', emailAddresses='am-operation@example.org'), + 1203=contact(label='Herr Philip Meyer-Contract , JM e.K.', emailAddresses='pm-partner@example.org'), + 1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') } """); - assertThat(persons.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace(""" { - 900000=person(personType='NATURAL', tradeName='', familyName='Mellies', givenName='Michael'), - 900001=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Contract', givenName='Philip'), - 900002=person(personType='LEGAL', tradeName='Test PS', familyName='Schmidt', givenName='Petra') + 1101=person(personType='NATURAL', tradeName='', familyName='Mellies', givenName='Michael'), + 1201=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Billing', givenName='Jenny'), + 1202=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Operation', givenName='Andrew'), + 1203=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Contract', givenName='Philip'), + 1301=person(personType='LEGAL', tradeName='Test PS', familyName='Schmidt', givenName='Petra') } """); - assertThat(debitors.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" { - 7=debitor(1000700: NATURAL Mellies, Michael: mih), - 10=debitor(1001000: LEGAL JM e.K.: xyz), - 12=debitor(1101200: LEGAL Test PS: xxx) + 17=debitor(1001700: NATURAL Mellies, Michael: mih), + 20=debitor(1002000: LEGAL JM e.K.: xyz), + 22=debitor(1102200: LEGAL Test PS: xxx) } """); - assertThat(memberships.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { - 7=Membership(10007, NATURAL Mellies, Michael, 1000700, [2000-12-06,), NONE), - 10=Membership(10010, LEGAL JM e.K., 1001000, [2000-12-06,2016-01-01), UNKNOWN), - 12=Membership(11012, LEGAL Test PS, 1101200, [2021-04-01,), NONE) + 17=Membership(10017, NATURAL Mellies, Michael, 1001700, [2000-12-06,), NONE), + 20=Membership(10020, LEGAL JM e.K., 1002000, [2000-12-06,2016-01-01), UNKNOWN), + 22=Membership(11022, LEGAL Test PS, 1102200, [2021-04-01,), NONE) } """); - assertThat(relationships.toString()).isEqualToIgnoringWhitespace(""" + assertThat(toFormattedString(relationships)).isEqualToIgnoringWhitespace(""" { 1101=rel(relAnchor='NATURAL Mellies, Michael', relType='OPERATIONS', relHolder='NATURAL Mellies, Michael', contact='Herr Michael Mellies '), 1202=rel(relAnchor='LEGAL JM e.K.', relType='OPERATIONS', relHolder='LEGAL JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), @@ -251,9 +253,7 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(postgresAdminUser) ) { - return; - } + assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(bankAccounts.toString()).isEqualToIgnoringWhitespace(""" { @@ -280,16 +280,14 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(postgresAdminUser) ) { - return; - } + assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(coopShares.toString()).isEqualToIgnoringWhitespace(""" { - 33443=CoopShareTransaction(10007, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), - 33451=CoopShareTransaction(10010, 2000-12-06, SUBSCRIPTION, 2, initial share subscription), - 33701=CoopShareTransaction(10007, 2005-01-10, SUBSCRIPTION, 40, increase), - 33810=CoopShareTransaction(10010, 2016-12-31, CANCELLATION, 22, membership ended) + 33443=CoopShareTransaction(10017, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), + 33451=CoopShareTransaction(10020, 2000-12-06, SUBSCRIPTION, 2, initial share subscription), + 33701=CoopShareTransaction(10017, 2005-01-10, SUBSCRIPTION, 40, increase), + 33810=CoopShareTransaction(10020, 2016-12-31, CANCELLATION, 22, membership ended) } """); } @@ -305,20 +303,18 @@ public class ImportOfficeTables extends ContextBasedTest { throw new RuntimeException(e); } - if ( !"admin".equals(postgresAdminUser) ) { - return; - } + assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace(""" { - 30000=CoopAssetsTransaction(10007, 2000-12-06, DEPOSIT, 1280.00, for subscription A), - 31000=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 128.00, for subscription B), - 32000=CoopAssetsTransaction(10007, 2005-01-10, DEPOSIT, 2560.00, for subscription C), - 33001=CoopAssetsTransaction(10007, 2005-01-10, TRANSFER, -512.00, for transfer to 10), - 33002=CoopAssetsTransaction(10010, 2005-01-10, ADOPTION, 512.00, for transfer from 7), - 34001=CoopAssetsTransaction(10010, 2016-12-31, CLEARING, -8.00, for cancellation D), - 34002=CoopAssetsTransaction(10010, 2016-12-31, DISBURSAL, -100.00, for cancellation D), - 34003=CoopAssetsTransaction(10010, 2016-12-31, LOSS, -20.00, for cancellation D) + 30000=CoopAssetsTransaction(10017, 2000-12-06, DEPOSIT, 1280.00, for subscription A), + 31000=CoopAssetsTransaction(10020, 2000-12-06, DEPOSIT, 128.00, for subscription B), + 32000=CoopAssetsTransaction(10017, 2005-01-10, DEPOSIT, 2560.00, for subscription C), + 33001=CoopAssetsTransaction(10017, 2005-01-10, TRANSFER, -512.00, for transfer to 10), + 33002=CoopAssetsTransaction(10020, 2005-01-10, ADOPTION, 512.00, for transfer from 7), + 34001=CoopAssetsTransaction(10020, 2016-12-31, CLEARING, -8.00, for cancellation D), + 34002=CoopAssetsTransaction(10020, 2016-12-31, DISBURSAL, -100.00, for cancellation D), + 34003=CoopAssetsTransaction(10020, 2016-12-31, LOSS, -20.00, for cancellation D) } """); } @@ -411,60 +407,66 @@ public class ImportOfficeTables extends ContextBasedTest { private void deleteTestDataFromHsOfficeTables() { jpaAttempt.transacted(() -> { context(rbacSuperuser); - em.createNativeQuery("DELETE FROM hs_office_relationship WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_coopassetstransaction WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_coopassetstransaction_legacy_id WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_coopsharestransaction WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_coopsharestransaction_legacy_id WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_membership WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_sepamandate WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_sepamandate_legacy_id WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_debitor WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_bankaccount WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_partner WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_partner_details WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_contact WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM hs_office_person WHERE true").executeUpdate(); + em.createNativeQuery("delete from hs_office_relationship where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_coopassetstransaction where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_coopassetstransaction_legacy_id where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_coopsharestransaction where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_coopsharestransaction_legacy_id where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_membership where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_sepamandate where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_sepamandate_legacy_id where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_debitor where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_partner where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_contact where true").executeUpdate(); + em.createNativeQuery("delete from hs_office_person where true").executeUpdate(); }).assertSuccessful(); } private void resetFromHsOfficeSequences() { jpaAttempt.transacted(() -> { context(rbacSuperuser); - em.createNativeQuery("ALTER SEQUENCE hs_office_contact_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); - em.createNativeQuery("ALTER SEQUENCE hs_office_coopassetstransaction_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); - em.createNativeQuery("ALTER SEQUENCE public.hs_office_coopsharestransaction_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); - em.createNativeQuery("ALTER SEQUENCE public.hs_office_partner_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); - em.createNativeQuery("ALTER SEQUENCE public.hs_office_sepamandate_legacy_id_seq RESTART WITH 1000000000;").executeUpdate(); + em.createNativeQuery("alter sequence hs_office_contact_legacy_id_seq restart with 1000000000;").executeUpdate(); + em.createNativeQuery("alter sequence hs_office_coopassetstransaction_legacy_id_seq restart with 1000000000;") + .executeUpdate(); + em.createNativeQuery("alter sequence public.hs_office_coopsharestransaction_legacy_id_seq restart with 1000000000;") + .executeUpdate(); + em.createNativeQuery("alter sequence public.hs_office_partner_legacy_id_seq restart with 1000000000;") + .executeUpdate(); + em.createNativeQuery("alter sequence public.hs_office_sepamandate_legacy_id_seq restart with 1000000000;") + .executeUpdate(); }); } private void deleteFromTestTables() { jpaAttempt.transacted(() -> { context(rbacSuperuser); - em.createNativeQuery("DELETE FROM test_domain WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM test_package WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM test_customer WHERE true").executeUpdate(); + em.createNativeQuery("delete from test_domain where true").executeUpdate(); + em.createNativeQuery("delete from test_package where true").executeUpdate(); + em.createNativeQuery("delete from test_customer where true").executeUpdate(); }).assertSuccessful(); } private void deleteFromRbacTables() { jpaAttempt.transacted(() -> { context(rbacSuperuser); - final var usersNotToDelete = em.createNativeQuery("SELECT name FROM rbacuser WHERE name like 'superuser-%'").getResultList(); - final var usersToDelete = em.createNativeQuery("SELECT name FROM rbacuser WHERE name not like 'superuser-%'").getResultList(); + final var usersNotToDelete = em.createNativeQuery("select name from rbacuser where name like 'superuser-%'") + .getResultList(); + final var usersToDelete = em.createNativeQuery("select name from rbacuser where name not like 'superuser-%'") + .getResultList(); System.getenv(); }); jpaAttempt.transacted(() -> { context(rbacSuperuser); -// em.createNativeQuery("DELETE FROM rbacobject WHERE objecttable like 'hs_%'").executeUpdate(); -// em.createNativeQuery("DELETE FROM rbacgrants WHERE true").executeUpdate(); -// em.createNativeQuery("DELETE FROM rbacpermission WHERE true").executeUpdate(); -// em.createNativeQuery("DELETE FROM rbacreference WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM rbacuser_rv WHERE name not like 'superuser-%'").executeUpdate(); - em.createNativeQuery("DELETE FROM tx_journal WHERE true").executeUpdate(); - em.createNativeQuery("DELETE FROM tx_context WHERE true").executeUpdate(); + // em.createNativeQuery("DELETE FROM rbacobject WHERE objecttable like 'hs_%'").executeUpdate(); + // em.createNativeQuery("DELETE FROM rbacgrants WHERE true").executeUpdate(); + // em.createNativeQuery("DELETE FROM rbacpermission WHERE true").executeUpdate(); + // em.createNativeQuery("DELETE FROM rbacreference WHERE true").executeUpdate(); + em.createNativeQuery("delete from rbacuser_rv where name not like 'superuser-%'").executeUpdate(); + em.createNativeQuery("delete from tx_journal where true").executeUpdate(); + em.createNativeQuery("delete from tx_context where true").executeUpdate(); }).assertSuccessful(); } @@ -499,6 +501,7 @@ public class ImportOfficeTables extends ContextBasedTest { return csvReader.readAll(); } } + public static Reader skippingEmptyAndCommentLines(Reader reader) throws IOException { try (var bufferedReader = new BufferedReader(reader); StringWriter writer = new StringWriter()) { @@ -523,10 +526,7 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - final var person = HsOfficePersonEntity.builder() - .personType(HsOfficePersonType.UNKNOWN) // TODO - .build(); - persons.put(personId++, person); + final var person = HsOfficePersonEntity.builder().build(); final var partner = HsOfficePartnerEntity.builder() .debitorNumberPrefix(rec.getInteger("member_id")) @@ -678,91 +678,141 @@ public class ImportOfficeTables extends ContextBasedTest { .map(this::trimAll) .map(row -> new Record(columns, row)) .forEach(rec -> { - final var contactId = rec.getInteger("contact_id"); + final var contactId = rec.getInteger("contact_id"); - if (rec.getString("roles").isBlank()) { - fail("empty roles assignment not allowed for contact_id: " + contactId); - } + if (rec.getString("roles").isBlank()) { + fail("empty roles assignment not allowed for contact_id: " + contactId); + } - final var partner = partners.get(rec.getInteger("bp_id")); - final var debitor = debitors.get(rec.getInteger("bp_id")); + final var partner = partners.get(rec.getInteger("bp_id")); + final var debitor = debitors.get(rec.getInteger("bp_id")); - final var person = partner.getPerson(); - person.setTradeName(rec.getString("firma")); - determinePersonType(person); - // TODO: title+salutation: add to person - person.setGivenName(rec.getString("first_name")); - person.setFamilyName(rec.getString("last_name")); + final var partnerPerson = partner.getPerson(); + if (rec.getString("roles").contains("partner")) { + initPerson(partner.getPerson(), rec); + } - final var contact = HsOfficeContactEntity.builder().build(); - contacts.put(contactId, initContact(contact, rec)); + HsOfficePersonEntity contactPerson = partnerPerson; + if (!StringUtils.equals(rec.getString("firma"), partnerPerson.getTradeName()) || + !StringUtils.equals(rec.getString("first_name"), partnerPerson.getGivenName()) || + !StringUtils.equals(rec.getString("last_name"), partnerPerson.getFamilyName())) { + contactPerson = initPerson(HsOfficePersonEntity.builder().build(), rec); + } - var imported = false; - if (rec.getString("roles").contains("partner")) { - assertThat(partner.getContact()).isNull(); - partner.setContact(contact); - imported = true; - } - if (rec.getString("roles").contains("billing")) { - assertThat(debitor.getBillingContact()).isNull(); - debitor.setBillingContact(contact); - imported = true; - } - if (rec.getString("roles").contains("operation")) { - final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partner.getPerson()) - .relHolder(person) - .contact(contact) - .relType(HsOfficeRelationshipType.OPERATIONS) - .build(); - relationships.put(contactId, rel); - imported = true; - } + final var contact = HsOfficeContactEntity.builder().build(); + initContact(contact, rec); + + if (rec.getString("roles").contains("partner")) { + assertThat(partner.getContact()).isNull(); + partner.setContact(contact); + } + if (rec.getString("roles").contains("billing")) { + assertThat(debitor.getBillingContact()).isNull(); + debitor.setBillingContact(contact); + } + if (rec.getString("roles").contains("operation")) { + final var rel = HsOfficeRelationshipEntity.builder() + .relAnchor(partnerPerson) + .relHolder(contactPerson) + .contact(contact) + .relType(HsOfficeRelationshipType.OPERATIONS) + .build(); + relationships.put(contactId, rel); + } if (rec.getString("roles").contains("contractual")) { final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partner.getPerson()) - .relHolder(person) + .relAnchor(partnerPerson) + .relHolder(contactPerson) .contact(contact) .relType(HsOfficeRelationshipType.REPRESENTATIVE) .build(); relationships.put(contactId, rel); - imported = true; } - if (!imported) { + if (rec.getString("roles").contains("ex-partner")) { final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partner.getPerson()) - .relHolder(person) + .relAnchor(partnerPerson) + .relHolder(contactPerson) .contact(contact) - .relType(HsOfficeRelationshipType.UNKNOWN) + .relType(HsOfficeRelationshipType.EX_PARTNER) .build(); relationships.put(contactId, rel); } + verifyContainsOnly(rec.getString("roles"), "partner", "ex-partner", "billing", "contractual", "operation"); }); } - private void determinePersonType(final HsOfficePersonEntity person) { + private HsOfficePersonEntity initPerson(final HsOfficePersonEntity person, final Record contactRecord) { + // TODO: title+salutation: add to person + person.setGivenName(contactRecord.getString("first_name")); + person.setFamilyName(contactRecord.getString("last_name")); + person.setTradeName(contactRecord.getString("firma")); + determinePersonType(person, contactRecord.getString("roles")); + + persons.put(contactRecord.getInteger("contact_id"), person); + return person; + } + + private static void determinePersonType(final HsOfficePersonEntity person, final String roles) { if (person.getTradeName().isBlank()) { person.setPersonType(HsOfficePersonType.NATURAL); + } else if (roles.contains("partner")) { + person.setPersonType(HsOfficePersonType.LEGAL); + } else if (roles.contains("contractual") && + !person.getFamilyName().isBlank() && !person.getGivenName().isBlank()) { + person.setPersonType(HsOfficePersonType.NATURAL); + } else if ( endsWithWord(person.getTradeName(), "e.K.", "e.G.", "eG", "GmbH", "AG") ) { + person.setPersonType(HsOfficePersonType.LEGAL); } else { - person.setPersonType(HsOfficePersonType.LEGAL); // TODO: add rules if we distinguish + // TODO: detect the other person types as soon as we've switche to the new person types + person.setPersonType(HsOfficePersonType.UNKNOWN); } } - private HsOfficeContactEntity initContact(final HsOfficeContactEntity contact, final Record rec) { - contacts.put(rec.getInteger("contact_id"), contact); + private static boolean endsWithWord(final String value, final String... endings) { + final var lowerCaseValue = value.toLowerCase(); + for( String ending: endings ) { + if (lowerCaseValue.endsWith(" " + ending.toLowerCase())) { + return true; + } + } + return false; + } + + private void verifyContainsOnly(final String roles, final String... allowedRoles) { + final var givenRolesSet = stream(roles.replace(" ", "").split(",")).collect(Collectors.toSet()); + final var allowedRolesSet = stream(allowedRoles).collect(Collectors.toSet()); + final var unexpectedRolesSet = new HashSet<>(givenRolesSet); + unexpectedRolesSet.removeAll(allowedRolesSet); + assertThat(unexpectedRolesSet).isEmpty(); + } + + private HsOfficeContactEntity initContact(final HsOfficeContactEntity contact, final Record contactRecord) { contact.setLabel(toLabel( - rec.getString("salut"), - rec.getString("title"), - rec.getString("first_name"), - rec.getString("last_name"), - rec.getString("firma"))); - contact.setEmailAddresses(rec.getString("email")); - contact.setPostalAddress(toAddress(rec)); - contact.setPhoneNumbers(toPhoneNumbers(rec)); + contactRecord.getString("salut"), + contactRecord.getString("title"), + contactRecord.getString("first_name"), + contactRecord.getString("last_name"), + contactRecord.getString("firma"))); + contact.setEmailAddresses(contactRecord.getString("email")); + contact.setPostalAddress(toAddress(contactRecord)); + contact.setPhoneNumbers(toPhoneNumbers(contactRecord)); + + contacts.put(contactRecord.getInteger("contact_id"), contact); return contact; } + private String toFormattedString(final Map map) { + if ( map.isEmpty() ) { + return "{}"; + } + return "{\n" + + map.keySet().stream() + .map(id -> " " + id + "=" + map.get(id).toString()) + .collect(Collectors.joining(",\n")) + + "\n}\n"; + } + private String[] trimAll(final String[] record) { for (int i = 0; i < record.length; ++i) { if (record[i] != null) { @@ -932,6 +982,7 @@ class Record { } class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { + private static boolean previousTestsPassed = true; public void testAborted(ExtensionContext context, Throwable cause) { diff --git a/src/test/resources/migration/asset-transactions.csv b/src/test/resources/migration/asset-transactions.csv index 1bde43fd..12c2c39c 100644 --- a/src/test/resources/migration/asset-transactions.csv +++ b/src/test/resources/migration/asset-transactions.csv @@ -1,9 +1,9 @@ member_asset_id; bp_id; date; action; amount; comment -30000; 7; 2000-12-06; PAYMENT; 1280.00; for subscription A -31000; 10; 2000-12-06; PAYMENT; 128.00; for subscription B -32000; 7; 2005-01-10; PAYMENT; 2560.00; for subscription C -33001; 7; 2005-01-10; HANDOVER; -512.00; for transfer to 10 -33002; 10; 2005-01-10; ADOPTION; 512.00; for transfer from 7 -34001; 10; 2016-12-31; CLEARING; -8.00; for cancellation D -34002; 10; 2016-12-31; PAYBACK; -100.00; for cancellation D -34003; 10; 2016-12-31; LOSS; -20.00; for cancellation D +30000; 17; 2000-12-06; PAYMENT; 1280.00; for subscription A +31000; 20; 2000-12-06; PAYMENT; 128.00; for subscription B +32000; 17; 2005-01-10; PAYMENT; 2560.00; for subscription C +33001; 17; 2005-01-10; HANDOVER; -512.00; for transfer to 10 +33002; 20; 2005-01-10; ADOPTION; 512.00; for transfer from 7 +34001; 20; 2016-12-31; CLEARING; -8.00; for cancellation D +34002; 20; 2016-12-31; PAYBACK; -100.00; for cancellation D +34003; 20; 2016-12-31; LOSS; -20.00; for cancellation D diff --git a/src/test/resources/migration/business-partners.csv b/src/test/resources/migration/business-partners.csv index bfe390f9..a31c2e9d 100644 --- a/src/test/resources/migration/business-partners.csv +++ b/src/test/resources/migration/business-partners.csv @@ -1,4 +1,4 @@ bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat -7;10007;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 -10;10010;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; -12;11012;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; +17;10017;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 +20;10020;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; +22;11022;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index 1dfd6e24..d357b137 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,12 +1,12 @@ 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 # eine natürliche Person -1101; 7; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; partner,billing,operation +1101; 17; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; partner,billing,operation # eine juristische Person mit drei separaten Ansprechpartnern -1201; 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 -1202; 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 -1203; 10; Herr; Philip; Meyer-Contract; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual +1201; 20; 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 +1202; 20; 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 +1203; 20; Herr; Philip; Meyer-Contract; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual # eine juristische Person mit nur einem Ansprechpartner -1301; 12; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation +1301; 22; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation diff --git a/src/test/resources/migration/sepa-mandates.csv b/src/test/resources/migration/sepa-mandates.csv index 9ee6720e..d8dde8be 100644 --- a/src/test/resources/migration/sepa-mandates.csv +++ b/src/test/resources/migration/sepa-mandates.csv @@ -1,3 +1,3 @@ sepa_mandat_id; bp_id; bank_customer; bank_name; bank_iban; bank_bic; mandat_ref; mandat_signed; mandat_since; mandat_until; mandat_used -234234; 7; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 -235662; 10; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-28; 2005-07-01; ; 2016-01-18 +234234; 17; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 +235662; 20; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-28; 2005-07-01; ; 2016-01-18 diff --git a/src/test/resources/migration/share-transactions.csv b/src/test/resources/migration/share-transactions.csv index de5df6af..fa561419 100644 --- a/src/test/resources/migration/share-transactions.csv +++ b/src/test/resources/migration/share-transactions.csv @@ -1,5 +1,5 @@ member_share_id;bp_id; date; action; quantity; comment -33443; 7; 2000-12-06; SUBSCRIPTION; 20; initial share subscription -33451; 10; 2000-12-06; SUBSCRIPTION; 2; initial share subscription -33701; 7; 2005-01-10; SUBSCRIPTION; 40; increase -33810; 10; 2016-12-31; UNSUBSCRIPTION; 22; membership ended +33443; 17; 2000-12-06; SUBSCRIPTION; 20; initial share subscription +33451; 20; 2000-12-06; SUBSCRIPTION; 2; initial share subscription +33701; 17; 2005-01-10; SUBSCRIPTION; 40; increase +33810; 20; 2016-12-31; UNSUBSCRIPTION; 22; membership ended -- 2.39.2 From a0abec6a85425ec1a75750543b206ff07ddfc211 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 18 Jan 2024 15:49:01 +0100 Subject: [PATCH 47/72] fix import-office-data alias / gw importOfficeData --- .aliases | 15 ++++++++------- build.gradle | 4 ++-- ...ortOfficeTables.java => ImportOfficeData.java} | 4 +--- 3 files changed, 11 insertions(+), 12 deletions(-) rename src/test/java/net/hostsharing/hsadminng/hs/office/migration/{ImportOfficeTables.java => ImportOfficeData.java} (99%) diff --git a/.aliases b/.aliases index bd563eb2..af334963 100644 --- a/.aliases +++ b/.aliases @@ -1,6 +1,6 @@ # For using the alias import-office-tables, # copy these exports to .environment (ignored by git) # and amend them according to your external DB: -export HSADMINNG_POSTGRES_JDBC_URL= +export HSADMINNG_POSTGRES_JDBC_URL=jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin export HSADMINNG_POSTGRES_ADMIN_PASSWORD= export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted @@ -45,19 +45,20 @@ postgresAutodoc () { } alias postgres-autodoc=postgresAutodoc -function importOfficeTables() { - export HSADMINNG_POSTGRES_JDBC=jdbc:postgresql://localhost:5432/postgres - export HSADMINNG_POSTGRES_ADMIN_USERNAME=hsh99_admin +function importOfficeData() { + export HSADMINNG_POSTGRES_JDBC=jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers + export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin export HSADMINNG_POSTGRES_ADMIN_PASSWORD=password - export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=hsh99_restricted + export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted + export HSADMINNG_SUPERUSER=superuser-alex@hostsharing.net if [ -f .environment ]; then source .environment fi - ./gradlew test --tests ImportOfficeTables -x pitest + ./gradlew importOfficeData } -alias import-office-tables=importOfficeTables +alias import-office-data=importOfficeData alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' diff --git a/build.gradle b/build.gradle index 20ad0442..5686c3df 100644 --- a/build.gradle +++ b/build.gradle @@ -301,7 +301,7 @@ jacocoTestCoverageVerification { } } -tasks.register('importTest', Test) { +tasks.register('importOfficeData', Test) { useJUnitPlatform { includeTags 'import' } @@ -309,7 +309,7 @@ tasks.register('importTest', Test) { group 'verification' description 'run the import jobs as tests' } -test.dependsOn tasks.importTest +test.dependsOn tasks.importOfficeData // pitest mutation testing diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java similarity index 99% rename from src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java rename to src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 2c2d6707..3a1bf345 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeTables.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -109,7 +109,7 @@ import static org.assertj.core.api.Fail.fail; @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(OrderedDependedTestsExtension.class) -public class ImportOfficeTables extends ContextBasedTest { +public class ImportOfficeData extends ContextBasedTest { @Value("${spring.datasource.username}") private String postgresAdminUser; @@ -129,8 +129,6 @@ 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; -- 2.39.2 From 5d298a498f773d51fbdedfce160f08375add15ba Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 12:39:03 +0100 Subject: [PATCH 48/72] auto generate representative relationship if no explicit contractor set, add vip-contact and fix db enum value for ex-partner --- .../HsOfficeRelationshipType.java | 1 + .../hs-office-relationship-schemas.yaml | 9 +- .../changelog/230-hs-office-relationship.sql | 8 +- .../hs/office/migration/ImportOfficeData.java | 141 +++++++++++------- src/test/resources/migration/contacts.csv | 13 +- .../resources/migration/sepa-mandates.csv | 5 +- 6 files changed, 109 insertions(+), 68 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java index 6d4efc84..e3955c7e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipType.java @@ -4,6 +4,7 @@ public enum HsOfficeRelationshipType { UNKNOWN, EX_PARTNER, REPRESENTATIVE, + VIP_CONTACT, ACCOUNTING, OPERATIONS } diff --git a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml index cfda18c0..cb865205 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-relationship-schemas.yaml @@ -6,9 +6,12 @@ components: HsOfficeRelationshipType: type: string enum: - - REPRESENTATIVE - - ACCOUNTING - - TECHNICAL + - UNKNOWN + - EX_PARTNER + - REPRESENTATIVE, + - VIP_CONTACT + - ACCOUNTING, + - OPERATIONS HsOfficeRelationship: type: object diff --git a/src/main/resources/db/changelog/230-hs-office-relationship.sql b/src/main/resources/db/changelog/230-hs-office-relationship.sql index d1d4a366..942813d9 100644 --- a/src/main/resources/db/changelog/230-hs-office-relationship.sql +++ b/src/main/resources/db/changelog/230-hs-office-relationship.sql @@ -4,7 +4,13 @@ --changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -CREATE TYPE HsOfficeRelationshipType AS ENUM ('UNKNOWN', 'EX-PARTNER', 'REPRESENTATIVE', 'ACCOUNTING', 'OPERATIONS'); +CREATE TYPE HsOfficeRelationshipType AS ENUM ( + 'UNKNOWN', + 'EX_PARTNER', + 'REPRESENTATIVE', + 'VIP_CONTACT', + 'ACCOUNTING', + 'OPERATIONS'); CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 3a1bf345..7bdf815b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -111,6 +111,8 @@ import static org.assertj.core.api.Fail.fail; @ExtendWith(OrderedDependedTestsExtension.class) public class ImportOfficeData extends ContextBasedTest { + static int relationshipId = 2000000; + @Value("${spring.datasource.username}") private String postgresAdminUser; @@ -172,7 +174,7 @@ public class ImportOfficeData extends ContextBasedTest { assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { 17=Membership(10017, null null, null, 1001700, [2000-12-06,), NONE), - 20=Membership(10020, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), + 20=Membership(10020, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), 22=Membership(11022, null null, null, 1102200, [2021-04-01,), NONE) } """); @@ -194,48 +196,54 @@ public class ImportOfficeData extends ContextBasedTest { assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" { 17=partner(NATURAL Mellies, Michael: Herr Michael Mellies ), - 20=partner(LEGAL JM e.K.: Herr Philip Meyer-Contract , JM e.K.), + 20=partner(LEGAL JM GmbH: Herr Philip Meyer-Contract , JM GmbH), 22=partner(LEGAL Test PS: Petra Schmidt , Test PS) } """); assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace(""" { 1101=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'), - 1201=contact(label='Frau Dr. Jenny Meyer-Billing , JM e.K.', emailAddresses='jm-billing@example.org'), - 1202=contact(label='Herr Andrew Meyer-Operation , JM e.K.', emailAddresses='am-operation@example.org'), - 1203=contact(label='Herr Philip Meyer-Contract , JM e.K.', emailAddresses='pm-partner@example.org'), + 1200=contact(label='JM e.K.', emailAddresses='jm-ex-partner@example.org'), + 1201=contact(label='Frau Dr. Jenny Meyer-Billing , JM GmbH', emailAddresses='jm-billing@example.org'), + 1202=contact(label='Herr Andrew Meyer-Operation , JM GmbH', emailAddresses='am-operation@example.org'), + 1203=contact(label='Herr Philip Meyer-Contract , JM GmbH', emailAddresses='pm-partner@example.org'), 1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com') } """); assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace(""" { 1101=person(personType='NATURAL', tradeName='', familyName='Mellies', givenName='Michael'), - 1201=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Billing', givenName='Jenny'), - 1202=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Operation', givenName='Andrew'), - 1203=person(personType='LEGAL', tradeName='JM e.K.', familyName='Meyer-Contract', givenName='Philip'), + 1200=person(personType='LEGAL', tradeName='JM e.K.', familyName='', givenName=''), + 1201=person(personType='LEGAL', tradeName='JM GmbH', familyName='Meyer-Billing', givenName='Jenny'), + 1202=person(personType='LEGAL', tradeName='JM GmbH', familyName='Meyer-Operation', givenName='Andrew'), + 1203=person(personType='LEGAL', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'), 1301=person(personType='LEGAL', tradeName='Test PS', familyName='Schmidt', givenName='Petra') } """); assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" { 17=debitor(1001700: NATURAL Mellies, Michael: mih), - 20=debitor(1002000: LEGAL JM e.K.: xyz), + 20=debitor(1002000: LEGAL JM GmbH: xyz), 22=debitor(1102200: LEGAL Test PS: xxx) } """); assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { 17=Membership(10017, NATURAL Mellies, Michael, 1001700, [2000-12-06,), NONE), - 20=Membership(10020, LEGAL JM e.K., 1002000, [2000-12-06,2016-01-01), UNKNOWN), + 20=Membership(10020, LEGAL JM GmbH, 1002000, [2000-12-06,2016-01-01), UNKNOWN), 22=Membership(11022, LEGAL Test PS, 1102200, [2021-04-01,), NONE) } """); assertThat(toFormattedString(relationships)).isEqualToIgnoringWhitespace(""" { - 1101=rel(relAnchor='NATURAL Mellies, Michael', relType='OPERATIONS', relHolder='NATURAL Mellies, Michael', contact='Herr Michael Mellies '), - 1202=rel(relAnchor='LEGAL JM e.K.', relType='OPERATIONS', relHolder='LEGAL JM e.K.', contact='Herr Andrew Meyer-Operation , JM e.K.'), - 1203=rel(relAnchor='LEGAL JM e.K.', relType='REPRESENTATIVE', relHolder='LEGAL JM e.K.', contact='Herr Philip Meyer-Contract , JM e.K.'), - 1301=rel(relAnchor='LEGAL Test PS', relType='REPRESENTATIVE', relHolder='LEGAL Test PS', contact='Petra Schmidt , Test PS') + 2000000=rel(relAnchor='NATURAL Mellies, Michael', relType='OPERATIONS', relHolder='NATURAL Mellies, Michael', contact='Herr Michael Mellies '), + 2000001=rel(relAnchor='LEGAL JM GmbH', relType='EX_PARTNER', relHolder='LEGAL JM e.K.', contact='JM e.K.'), + 2000002=rel(relAnchor='LEGAL JM GmbH', relType='OPERATIONS', relHolder='LEGAL JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), + 2000003=rel(relAnchor='LEGAL JM GmbH', relType='VIP_CONTACT', relHolder='LEGAL JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), + 2000004=rel(relAnchor='LEGAL JM GmbH', relType='REPRESENTATIVE', relHolder='LEGAL JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), + 2000005=rel(relAnchor='LEGAL Test PS', relType='OPERATIONS', relHolder='LEGAL Test PS', contact='Petra Schmidt , Test PS'), + 2000006=rel(relAnchor='LEGAL Test PS', relType='REPRESENTATIVE', relHolder='LEGAL Test PS', contact='Petra Schmidt , Test PS'), + 2000007=rel(relAnchor='NATURAL Mellies, Michael', relType='REPRESENTATIVE', relHolder='NATURAL Mellies, Michael', contact='Herr Michael Mellies ') } """); } @@ -253,18 +261,20 @@ public class ImportOfficeData extends ContextBasedTest { assumeThat(postgresAdminUser).isEqualTo("admin"); - assertThat(bankAccounts.toString()).isEqualToIgnoringWhitespace(""" - { - 234234=bankAccount(holder='Michael Mellies', iban='DE37500105177419788228', bic='INGDDEFFXXX'), - 235662=bankAccount(holder='JM e.K.', iban='DE49500105174516484892', bic='INGDDEFFXXX') - } - """); - assertThat(sepaMandates.toString()).isEqualToIgnoringWhitespace(""" - { - 234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)), - 235662=SEPA-Mandate(DE49500105174516484892, JM33344, 2005-06-28, [2005-07-01,)) - } - """); + assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace(""" + { + 234234=bankAccount(holder='Michael Mellies', iban='DE37500105177419788228', bic='INGDDEFFXXX'), + 235600=bankAccount(holder='JM e.K.', iban='DE02300209000106531065', bic='CMCIDEDD'), + 235662=bankAccount(holder='JM GmbH', iban='DE49500105174516484892', bic='INGDDEFFXXX') + } + """); + assertThat(toFormattedString(sepaMandates)).isEqualToIgnoringWhitespace(""" + { + 234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)), + 235600=SEPA-Mandate(DE02300209000106531065, JM33344, 2004-01-15, [2004-01-20,2005-06-28)), + 235662=SEPA-Mandate(DE49500105174516484892, JM33344, 2005-06-28, [2005-07-01,)) + } + """); } @Test @@ -686,7 +696,7 @@ public class ImportOfficeData extends ContextBasedTest { final var debitor = debitors.get(rec.getInteger("bp_id")); final var partnerPerson = partner.getPerson(); - if (rec.getString("roles").contains("partner")) { + if (containsRole(rec)) { initPerson(partner.getPerson(), rec); } @@ -700,43 +710,62 @@ public class ImportOfficeData extends ContextBasedTest { final var contact = HsOfficeContactEntity.builder().build(); initContact(contact, rec); - if (rec.getString("roles").contains("partner")) { + if (containsRole(rec, "partner")) { assertThat(partner.getContact()).isNull(); partner.setContact(contact); } - if (rec.getString("roles").contains("billing")) { + if (containsRole(rec, "billing")) { assertThat(debitor.getBillingContact()).isNull(); debitor.setBillingContact(contact); } - if (rec.getString("roles").contains("operation")) { - final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partnerPerson) - .relHolder(contactPerson) - .contact(contact) - .relType(HsOfficeRelationshipType.OPERATIONS) - .build(); - relationships.put(contactId, rel); + if (containsRole(rec, "operation")) { + addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.OPERATIONS); } - if (rec.getString("roles").contains("contractual")) { - final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partnerPerson) - .relHolder(contactPerson) - .contact(contact) - .relType(HsOfficeRelationshipType.REPRESENTATIVE) - .build(); - relationships.put(contactId, rel); + if (containsRole(rec, "contractual")) { + addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.REPRESENTATIVE); } - if (rec.getString("roles").contains("ex-partner")) { - final var rel = HsOfficeRelationshipEntity.builder() - .relAnchor(partnerPerson) - .relHolder(contactPerson) - .contact(contact) - .relType(HsOfficeRelationshipType.EX_PARTNER) - .build(); - relationships.put(contactId, rel); + if (containsRole(rec, "ex-partner")) { + addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.EX_PARTNER); } - verifyContainsOnly(rec.getString("roles"), "partner", "ex-partner", "billing", "contractual", "operation"); + if (containsRole(rec, "vip-contact")) { + addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT); + } + verifyContainsOnly(rec.getString("roles"), "partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"); }); + + optionallyAddMissingContractualRelationships(); + } + + private static void optionallyAddMissingContractualRelationships() { + partners.forEach( (id, partner) -> { + final var partnerPerson = partner.getPerson(); + if (relationships.values().stream().filter(rel -> rel.getRelHolder() == partnerPerson && rel.getRelType() == HsOfficeRelationshipType.REPRESENTATIVE).findFirst().isEmpty()) { + addRelationship(partnerPerson, partnerPerson, partner.getContact(), HsOfficeRelationshipType.REPRESENTATIVE); + } + }); + } + + private static boolean containsRole(final Record rec, final String role) { + final var roles = rec.getString("roles"); + return ("," + roles + ",").contains("," + role + ","); + } + + private static boolean containsRole(final Record rec) { + return containsRole(rec, "partner"); + } + + private static void addRelationship( + final HsOfficePersonEntity partnerPerson, + final HsOfficePersonEntity contactPerson, + final HsOfficeContactEntity contact, + final HsOfficeRelationshipType representative) { + final var rel = HsOfficeRelationshipEntity.builder() + .relAnchor(partnerPerson) + .relHolder(contactPerson) + .contact(contact) + .relType(representative) + .build(); + relationships.put(relationshipId++, rel); } private HsOfficePersonEntity initPerson(final HsOfficePersonEntity person, final Record contactRecord) { @@ -880,8 +909,8 @@ public class ImportOfficeData extends ContextBasedTest { result.append(firstname + " "); if (isNotBlank(lastname)) result.append(lastname + " "); - if (result.length() > 0 && isNotBlank(firm)) { - result.append(", " + firm); + if (isNotBlank(firm)) { + result.append( (isBlank(result) ? "" : ", ") + firm); } return result.toString(); } diff --git a/src/test/resources/migration/contacts.csv b/src/test/resources/migration/contacts.csv index d357b137..af67bce3 100644 --- a/src/test/resources/migration/contacts.csv +++ b/src/test/resources/migration/contacts.csv @@ -1,12 +1,13 @@ 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 -# eine natürliche Person +# eine natürliche Person, implizites contractual 1101; 17; Herr; Michael; Mellies; ; ; ; Kleine Freiheit 50; 26524; Hage; DE; ; +49 4931 123456; +49 1522 123456;; mih@example.org; partner,billing,operation -# eine juristische Person mit drei separaten Ansprechpartnern -1201; 20; 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 -1202; 20; 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 -1203; 20; Herr; Philip; Meyer-Contract; ; JM e.K.;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual +# eine juristische Person mit drei separaten Ansprechpartnern, vip-contact und ex-partner +1200; 20;; ; ; ; JM e.K.;; Wiesenweg 15; 12335; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; jm-ex-partner@example.org; ex-partner +1201; 20; Frau; Jenny; Meyer-Billing; Dr.; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing +1202; 20; Herr; Andrew; Meyer-Operation; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation,vip-contact +1203; 20; Herr; Philip; Meyer-Contract; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual -# eine juristische Person mit nur einem Ansprechpartner +# eine juristische Person mit nur einem Ansprechpartner und explizitem contractual 1301; 22; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation diff --git a/src/test/resources/migration/sepa-mandates.csv b/src/test/resources/migration/sepa-mandates.csv index d8dde8be..a76adc16 100644 --- a/src/test/resources/migration/sepa-mandates.csv +++ b/src/test/resources/migration/sepa-mandates.csv @@ -1,3 +1,4 @@ sepa_mandat_id; bp_id; bank_customer; bank_name; bank_iban; bank_bic; mandat_ref; mandat_signed; mandat_since; mandat_until; mandat_used -234234; 17; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 -235662; 20; JM e.K.; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-28; 2005-07-01; ; 2016-01-18 +234234; 17; Michael Mellies; ING Bank AG; DE37500105177419788228; INGDDEFFXXX; MH12345; 2004-06-12; 2004-06-15; ; 2022-10-20 +235600; 20; JM e.K.; Targobank AG; DE02300209000106531065; CMCIDEDD; JM33344; 2004-01-15; 2004-01-20;2005-06-27 ;2016-01-18 +235662; 20; JM GmbH; ING Bank AG; DE49500105174516484892; INGDDEFFXXX; JM33344; 2005-06-28; 2005-07-01; ; 2016-01-18 -- 2.39.2 From a382c141b3b60f63858404bd82dc4b7da3795a82 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 13:40:34 +0100 Subject: [PATCH 49/72] name alias gw-importOfficeData and fix test task dependency --- .aliases | 2 +- build.gradle | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.aliases b/.aliases index af334963..948a58bb 100644 --- a/.aliases +++ b/.aliases @@ -58,7 +58,7 @@ function importOfficeData() { ./gradlew importOfficeData } -alias import-office-data=importOfficeData +alias gw-importOfficeData=importOfficeData alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' diff --git a/build.gradle b/build.gradle index 5686c3df..285fa8d9 100644 --- a/build.gradle +++ b/build.gradle @@ -309,7 +309,6 @@ tasks.register('importOfficeData', Test) { group 'verification' description 'run the import jobs as tests' } -test.dependsOn tasks.importOfficeData // pitest mutation testing -- 2.39.2 From 6d4ba9b094ef63e602f443c117a8cd9d396c17de Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 13:40:58 +0100 Subject: [PATCH 50/72] add missing membershipfeebillable to DB, OpenAPI and Patcher --- .../membership/HsOfficeMembershipEntity.java | 4 ++-- .../HsOfficeMembershipEntityPatcher.java | 2 ++ .../hs-office/hs-office-membership-schemas.yaml | 9 +++++++++ .../db/changelog/300-hs-office-membership.sql | 2 +- .../changelog/303-hs-office-membership-rbac.sql | 2 +- ...fficeMembershipControllerAcceptanceTest.java | 4 +++- .../HsOfficeMembershipControllerRestTest.java | 12 ++++++++---- ...HsOfficeMembershipEntityPatcherUnitTest.java | 17 ++++++++++------- ...ficeMembershipRepositoryIntegrationTest.java | 3 +++ 9 files changed, 39 insertions(+), 16 deletions(-) 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 14528c2f..b5846324 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 @@ -59,8 +59,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @Type(PostgreSQLRangeType.class) private Range validity; - @Column(name = "membership_fee_billable") - private boolean membershipFeeBillable; + @Column(name = "membershipfeebillable", nullable = false) + private Boolean membershipFeeBillable; // not primitive to force setting the value @Column(name = "reasonfortermination") @Enumerated(EnumType.STRING) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java index f163434f..59fa6070 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcher.java @@ -37,6 +37,8 @@ public class HsOfficeMembershipEntityPatcher implements EntityPatcher mapper.map(v, HsOfficeReasonForTermination.class)) .ifPresent(entity::setReasonForTermination); + OptionalFromJson.of(resource.getMembershipFeeBillable()).ifPresent( + entity::setMembershipFeeBillable); } private void verifyNotNull(final UUID newValue, final String propertyName) { diff --git a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml index fd27412a..74c8143b 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml @@ -33,6 +33,8 @@ components: format: date reasonForTermination: $ref: '#/components/schemas/HsOfficeReasonForTermination' + membershipFeeBillable: + type: boolean HsOfficeMembershipPatch: type: object @@ -48,6 +50,9 @@ components: reasonForTermination: nullable: true $ref: '#/components/schemas/HsOfficeReasonForTermination' + membershipFeeBillable: + nullable: true + type: boolean additionalProperties: false HsOfficeMembershipInsert: @@ -74,8 +79,12 @@ components: nullable: true reasonForTermination: $ref: '#/components/schemas/HsOfficeReasonForTermination' + membershipFeeBillable: + nullable: false + type: boolean required: - partnerUuid - mainDebitorUuid - validFrom + - membershipFeeBillable additionalProperties: false diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index 69f21799..12f2dd34 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -16,7 +16,7 @@ create table if not exists hs_office_membership memberNumber numeric(5) not null unique, validity daterange not null, reasonForTermination HsOfficeReasonForTermination not null default 'NONE', - membership_fee_billable boolean not null default true + membershipFeeBillable boolean not null default true ); --// diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index 5f749445..972021e7 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -106,7 +106,7 @@ call generateRbacRestrictedView('hs_office_membership', columnUpdates => $updates$ validity = new.validity, reasonForTermination = new.reasonForTermination, - membership_fee_billable = new.membership_fee_billable + membershipFeeBillable = new.membershipFeeBillable $updates$); --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 199d09f6..243a6fe2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -131,7 +131,8 @@ class HsOfficeMembershipControllerAcceptanceTest { "partnerUuid": "%s", "mainDebitorUuid": "%s", "memberNumber": 20001, - "validFrom": "2022-10-13" + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" } """.formatted(givenPartner.getUuid(), givenDebitor.getUuid())) .port(port) @@ -446,6 +447,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .memberNumber(++tempMemberNumber) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .reasonForTermination(NONE) + .membershipFeeBillable(true) .build(); return membershipRepo.save(newMembership); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index a2aebfce..2f42cb58 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -72,7 +72,8 @@ public class HsOfficeMembershipControllerRestTest { "partnerUuid": null, "mainDebitorUuid": "%s", "memberNumber": 20001, - "validFrom": "2022-10-13" + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" } """.formatted(UUID.randomUUID())) .accept(MediaType.APPLICATION_JSON)) @@ -97,7 +98,8 @@ public class HsOfficeMembershipControllerRestTest { "partnerUuid": "%s", "mainDebitorUuid": null, "memberNumber": 20001, - "validFrom": "2022-10-13" + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" } """.formatted(UUID.randomUUID())) .accept(MediaType.APPLICATION_JSON)) @@ -128,7 +130,8 @@ public class HsOfficeMembershipControllerRestTest { "partnerUuid": "%s", "mainDebitorUuid": "%s", "memberNumber": 20001, - "validFrom": "2022-10-13" + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" } """.formatted(givenPartnerUuid, givenMainDebitorUuid)) .accept(MediaType.APPLICATION_JSON)) @@ -159,7 +162,8 @@ public class HsOfficeMembershipControllerRestTest { "partnerUuid": "%s", "mainDebitorUuid": "%s", "memberNumber": 20001, - "validFrom": "2022-10-13" + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" } """.formatted(givenPartnerUuid, givenMainDebitorUuid)) .accept(MediaType.APPLICATION_JSON)) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java index 25f68f4b..ee4944c1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityPatcherUnitTest.java @@ -36,6 +36,9 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase< private static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-04-15"); private static final LocalDate PATCHED_VALID_TO = LocalDate.parse("2022-12-31"); + private static final Boolean GIVEN_MEMBERSHIP_FEE_BILLABLE = true; + private static final Boolean PATCHED_MEMBERSHIP_FEE_BILLABLE = false; + @Mock private EntityManager em; @@ -56,6 +59,7 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase< entity.setMainDebitor(TEST_DEBITOR); entity.setPartner(TEST_PARTNER); entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM)); + entity.setMembershipFeeBillable(GIVEN_MEMBERSHIP_FEE_BILLABLE); return entity; } @@ -90,7 +94,12 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeReasonForTerminationResource.CANCELLATION, HsOfficeMembershipEntity::setReasonForTermination, HsOfficeReasonForTermination.CANCELLATION) - .notNullable() + .notNullable(), + new JsonNullableProperty<>( + "membershipFeeBillable", + HsOfficeMembershipPatchResource::setMembershipFeeBillable, + PATCHED_MEMBERSHIP_FEE_BILLABLE, + HsOfficeMembershipEntity::setMembershipFeeBillable) ); } @@ -99,10 +108,4 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase< newDebitor.setUuid(uuid); return newDebitor; } - - private HsOfficeMembershipEntity newMembership(final UUID uuid) { - final var newMembership = new HsOfficeMembershipEntity(); - newMembership.setUuid(uuid); - return newMembership; - } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 50f18b64..c6de2a44 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -81,6 +81,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) + .membershipFeeBillable(true) .build()); return membershipRepo.save(newMembership); }); @@ -111,6 +112,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) + .membershipFeeBillable(true) .build()); return membershipRepo.save(newMembership); }); @@ -416,6 +418,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) + .membershipFeeBillable(true) .build(); toCleanup(newMembership); -- 2.39.2 From d4785b472c26f9593b0d8ce308d544fc7317d5cf Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 13:46:39 +0100 Subject: [PATCH 51/72] add missing birthPlace to OpenAPI and Patcher --- .../HsOfficePartnerDetailsEntityPatcher.java | 1 + .../hs-office/hs-office-partner-schemas.yaml | 9 +++++++++ ...fficePartnerDetailsEntityPatcherUnitTest.java | 16 ++++++++++++++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityPatcher.java index 5940339a..61cd49c0 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntityPatcher.java @@ -22,6 +22,7 @@ class HsOfficePartnerDetailsEntityPatcher implements EntityPatcher { private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); - private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); - private static final UUID INITIAL_PERSON_UUID = UUID.randomUUID(); + + private static final String INITIAL_BIRTHPLACE = null; + private static final String PATCHED_BIRTHPLACE = "Essen (Ruhr)"; private static final LocalDate INITIAL_BIRTHDAY = LocalDate.parse("1900-01-01"); private static final LocalDate PATCHED_BIRTHDAY = LocalDate.parse("1990-12-31"); @@ -54,6 +55,7 @@ class HsOfficePartnerDetailsEntityPatcherUnitTest extends PatchUnitTestBase< entity.setUuid(INITIAL_PARTNER_UUID); entity.setRegistrationOffice("initial Reg-Office"); entity.setRegistrationNumber("initial Reg-Number"); + entity.setBirthPlace(INITIAL_BIRTHPLACE); entity.setBirthday(INITIAL_BIRTHDAY); entity.setBirthName("initial birth name"); entity.setDateOfDeath(INITIAL_DAY_OF_DEATH); @@ -78,6 +80,16 @@ class HsOfficePartnerDetailsEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficePartnerDetailsPatchResource::setRegistrationOffice, "patched Reg-Office", HsOfficePartnerDetailsEntity::setRegistrationOffice), + new JsonNullableProperty<>( + "birthplace", + HsOfficePartnerDetailsPatchResource::setBirthPlace, + PATCHED_BIRTHPLACE, + HsOfficePartnerDetailsEntity::setBirthPlace), + new JsonNullableProperty<>( + "birthname", + HsOfficePartnerDetailsPatchResource::setBirthName, + "patched birth name", + HsOfficePartnerDetailsEntity::setBirthName), new JsonNullableProperty<>( "birthday", HsOfficePartnerDetailsPatchResource::setBirthday, -- 2.39.2 From 06a39c421e196e12c1322fb9483360a2737c67ef Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 14:09:23 +0100 Subject: [PATCH 52/72] add missing debitorNumberPrefix to OpenAPI - not patchable, though --- .../hs-office/hs-office-partner-schemas.yaml | 11 +++++++++++ .../db/changelog/223-hs-office-partner-rbac.sql | 1 + .../HsOfficePartnerControllerAcceptanceTest.java | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml index 6aede37b..6b044a27 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml @@ -9,6 +9,11 @@ components: uuid: type: string format: uuid + debitorNumberPrefix: + type: integer + format: int8 + minimum: 10000 + maximum: 99999 person: $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson' contact: @@ -86,6 +91,11 @@ components: HsOfficePartnerInsert: type: object properties: + debitorNumberPrefix: + type: integer + format: int8 + minimum: 10000 + maximum: 99999 personUuid: type: string format: uuid @@ -95,6 +105,7 @@ components: details: $ref: '#/components/schemas/HsOfficePartnerDetailsInsert' required: + - debitorNumberPrefix - personUuid - contactUuid - details diff --git a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql index 32f629dc..db5db365 100644 --- a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql @@ -180,6 +180,7 @@ call generateRbacIdentityView('hs_office_partner', $idName$ call generateRbacRestrictedView('hs_office_partner', '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', $updates$ + debitorNumberPrefix = new.debitorNumberPrefix, personUuid = new.personUuid, contactUuid = new.contactUuid $updates$); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java index 053b03e1..0ac1eb2d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java @@ -125,6 +125,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { + "debitorNumberPrefix": "12345", "contactUuid": "%s", "personUuid": "%s", "details": { @@ -166,6 +167,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { + "debitorNumberPrefix": "12345", "contactUuid": "%s", "personUuid": "%s", "details": {} @@ -193,6 +195,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { + "debitorNumberPrefix": "12345", "contactUuid": "%s", "personUuid": "%s", "details": {} @@ -294,6 +297,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { + "debitorNumerPrefix": "12345", "contactUuid": "%s", "personUuid": "%s", "details": { -- 2.39.2 From d069e87c01d41cba371a9f1c80e510d6ac70f627 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Fri, 19 Jan 2024 14:35:08 +0100 Subject: [PATCH 53/72] add missing billable and vatReverseCharge to OpenAPI and Patcher --- .../office/debitor/HsOfficeDebitorEntity.java | 4 +-- .../debitor/HsOfficeDebitorEntityPatcher.java | 8 ++--- .../hs-office/hs-office-debitor-schemas.yaml | 17 +++++++++- ...OfficeDebitorControllerAcceptanceTest.java | 12 ++++++- .../HsOfficeDebitorEntityPatcherUnitTest.java | 32 +++++++++++++++++-- ...fficeDebitorRepositoryIntegrationTest.java | 32 ++----------------- 6 files changed, 65 insertions(+), 40 deletions(-) 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 6f0ee297..332ea8dd 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 @@ -53,8 +53,8 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { @JoinColumn(name = "billingcontactuuid") private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson - @Column(name = "billable") - private boolean billable; + @Column(name = "billable", nullable = false) + private Boolean billable; // not a primitive because otherwise the default would be false @Column(name = "vatid") private String vatId; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java index 2565ae30..092a0a9b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java @@ -6,6 +6,7 @@ import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.OptionalFromJson; import jakarta.persistence.EntityManager; +import java.util.Optional; class HsOfficeDebitorEntityPatcher implements EntityPatcher { @@ -25,12 +26,11 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher { - verifyNotNull(newValue, "vatBusiness"); - entity.setVatBusiness(newValue); - }); + Optional.ofNullable(resource.getVatBusiness()).ifPresent(entity::setVatBusiness); + Optional.ofNullable(resource.getVatReverseCharge()).ifPresent(entity::setVatReverseCharge); OptionalFromJson.of(resource.getDefaultPrefix()).ifPresent(newValue -> { verifyNotNull(newValue, "defaultPrefix"); entity.setDefaultPrefix(newValue); diff --git a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml index 372b57bf..26736fac 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-debitor-schemas.yaml @@ -23,6 +23,8 @@ components: $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' billingContact: $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' + billable: + type: boolean vatId: type: string vatCountryCode: @@ -30,6 +32,8 @@ components: pattern: '^[A-Z][A-Z]$' vatBusiness: type: boolean + vatReverseCharge: + type: boolean refundBankAccount: $ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount' defaultPrefix: @@ -43,6 +47,9 @@ components: type: string format: uuid nullable: true + billable: + type: boolean + nullable: false vatId: type: string nullable: true @@ -52,7 +59,10 @@ components: nullable: true vatBusiness: type: boolean - nullable: true + nullable: false + vatReverseCharge: + type: boolean + nullable: false refundBankAccountUuid: type: string format: uuid @@ -78,6 +88,8 @@ components: format: int8 minimum: 00 maximum: 99 + billable: + type: boolean vatId: type: string vatCountryCode: @@ -85,6 +97,8 @@ components: pattern: '^[A-Z][A-Z]$' vatBusiness: type: boolean + vatReverseCharge: + type: boolean refundBankAccountUuid: type: string format: uuid @@ -96,3 +110,4 @@ components: - partnerUuid - billingContactUuid - defaultPrefix + - billable diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java index cba8e203..1de3bb06 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorControllerAcceptanceTest.java @@ -164,9 +164,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "refundBankAccountUuid": "%s", "defaultPrefix": "for" } @@ -208,7 +210,9 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", - "defaultPrefix": "for" + "defaultPrefix": "for", + "billable": "true", + "vatReverseCharge": "false" } """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix)) .port(port) @@ -250,9 +254,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "defaultPrefix": "thi" } """ @@ -282,9 +288,11 @@ class HsOfficeDebitorControllerAcceptanceTest { "partnerUuid": "%s", "billingContactUuid": "%s", "debitorNumberSuffix": "%s", + "billable": "true", "vatId": "VAT123456", "vatCountryCode": "DE", "vatBusiness": true, + "vatReverseCharge": "false", "defaultPrefix": "for" } """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix)) @@ -535,9 +543,11 @@ class HsOfficeDebitorControllerAcceptanceTest { final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newDebitor = HsOfficeDebitorEntity.builder() .debitorNumberSuffix(++nextDebitorSuffix) + .billable(true) .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .vatReverseCharge(false) .build(); return debitorRepo.save(newDebitor); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java index 8703ccdb..d94e9323 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java @@ -36,6 +36,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< private static final boolean PATCHED_VAT_BUSINESS = false; + private static final boolean INITIAL_BILLABLE = false; + private static final boolean PATCHED_BILLABLE = true; + + private static final boolean INITIAL_VAT_REVERSE_CHARGE = true; + private static final boolean PATCHED_VAT_REVERSE_CHARGE = false; + private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder() .uuid(INITIAL_PARTNER_UUID) .build(); @@ -60,9 +66,11 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< entity.setUuid(INITIAL_DEBITOR_UUID); entity.setPartner(givenInitialPartner); entity.setBillingContact(givenInitialContact); + entity.setBillable(INITIAL_BILLABLE); entity.setVatId("initial VAT-ID"); entity.setVatCountryCode("AA"); entity.setVatBusiness(true); + entity.setVatReverseCharge(INITIAL_VAT_REVERSE_CHARGE); entity.setDefaultPrefix("abc"); return entity; } @@ -87,6 +95,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorEntity::setBillingContact, newBillingContact(PATCHED_CONTACT_UUID)) .notNullable(), + new SimpleProperty<>( + "personType", + HsOfficeDebitorPatchResource::setBillable, + PATCHED_BILLABLE, + HsOfficeDebitorEntity::setBillable) + .notNullable(), new JsonNullableProperty<>( "vatId", HsOfficeDebitorPatchResource::setVatId, @@ -97,12 +111,24 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorPatchResource::setVatCountryCode, PATCHED_VAT_COUNTRY_CODE, HsOfficeDebitorEntity::setVatCountryCode), - new JsonNullableProperty<>( - "vatBusiness", + new SimpleProperty<>( + "vatReverseCharge", + HsOfficeDebitorPatchResource::setVatReverseCharge, + PATCHED_VAT_REVERSE_CHARGE, + HsOfficeDebitorEntity::setVatReverseCharge) + .notNullable(), + new SimpleProperty<>( + "personType", HsOfficeDebitorPatchResource::setVatBusiness, - PATCHED_VAT_BUSINESS, + PATCHED_BILLABLE, HsOfficeDebitorEntity::setVatBusiness) .notNullable(), + new SimpleProperty<>( + "personType", + HsOfficeDebitorPatchResource::setVatReverseCharge, + PATCHED_BILLABLE, + HsOfficeDebitorEntity::setVatReverseCharge) + .notNullable(), new JsonNullableProperty<>( "defaultPrefix", HsOfficeDebitorPatchResource::setDefaultPrefix, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index b6fb9332..2b12cffe 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -85,6 +85,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .billable(false) .build(); return debitorRepo.save(newDebitor); }); @@ -96,33 +97,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { assertThat(debitorRepo.count()).isEqualTo(count + 1); } -// @ParameterizedTest -// @ValueSource(strings = {"", "a", "ab", "a12", "123", "12a"}) -// public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) { -// // given -// context("superuser-alex@hostsharing.net"); -// final var count = debitorRepo.count(); -// final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); -// final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); -// -// // when -// final var result = attempt(em, () -> { -// final var newDebitor = HsOfficeDebitorEntity.builder() -// .debitorNumberSuffix((byte)21) -// .partner(givenPartner) -// .billingContact(givenContact) -// .defaultPrefix(givenPrefix) -// .build(); -// return debitorRepo.save(newDebitor); -// }); -// -// // then -// result.assertSuccessful(); -// assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeDebitorEntity::getUuid).isNotNull(); -// assertThatDebitorIsPersisted(result.returnedValue()); -// assertThat(debitorRepo.count()).isEqualTo(count + 1); -// } - @Test public void createsAndGrantsRoles() { // given @@ -131,7 +105,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream() // some search+replace to make the output fit into the screen width .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) -// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) @@ -147,6 +120,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .partner(givenPartner) .billingContact(givenContact) .defaultPrefix("abc") + .billable(false) .build(); return debitorRepo.save(newDebitor); }).assertSuccessful(); @@ -161,7 +135,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { "hs_office_debitor#1000422:Fourthe.G.-forthcontact.guest")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) -// .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("forthcontact", "4th")) @@ -584,6 +557,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { .billingContact(givenContact) .refundBankAccount(givenBankAccount) .defaultPrefix(defaultPrefix) + .billable(true) .build(); return debitorRepo.save(newDebitor); -- 2.39.2 From 837e7fee97ef33198f18f1e43ca0b6eb65f96d7d Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 20 Jan 2024 13:09:41 +0100 Subject: [PATCH 54/72] in target diagram: introduce partner-person partner-role to Hostsharing eG --- doc/hs-office-data-structure.md | 115 ++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md index e5d83316..56467c5d 100644 --- a/doc/hs-office-data-structure.md +++ b/doc/hs-office-data-structure.md @@ -4,31 +4,69 @@ classDiagram direction TD - namespace Partner { - - class partner-MeierGmbH - class personDetails-MeierGmbH - class contactData-MeierGmbH - class person-MeierGmbH - - class person-FrankMeier - class contactData-FrankMeier - class role-MeierGmbH-FrankMeier + namespace Hostsharing { + class person-HostsharingEG + } - class person-SabineMeier - class contactData-SabineMeier - class role-MeierGmbH-SabineMeier - + namespace Partner { + class partner-MeierGmbH + class role-MeierGmbH + class personDetails-MeierGmbH + class contactData-MeierGmbH + class person-MeierGmbH + } + + namespace Representatives { + class person-FrankMeier + class contactData-FrankMeier + class role-MeierGmbH-FrankMeier + } + + namespace Debitors { class debitor-MeierGmbH class contactData-MeierGmbH-Buha class role-MeierGmbH-Buha } - class partner-MeierGmbH { - +partnerNumber: 12345 - +person + namespace Operations { + class person-SabineMeier + class contactData-SabineMeier + class role-MeierGmbH-SabineMeier } - partner-MeierGmbH o-- person-MeierGmbH + + namespace Enums { + + class RoleType { + <> + UNKNOWN + REPRESENTATIVE + ACCOUNTING + OPERATIONS + } + + class PersonType { + <> + UNKNOWN: nur für Import + NATURAL_PERSON: natürliche Person + LEGAL_PERSON: z.B. GmbH, e.K., eG, e.V. + INCORORATED_FIRM: z.B. OHG, Partnerschaftsgesellschaft + UNINCORPORATED_FIRM: z.B. GbR, ARGE, Erbengemeinschaft + PUBLIC_INSTITUTION: KdöR, AöR [ohne Registergericht/Registernummer] + } + } + + class person-HostsharingEG { + +personType: LEGAL + +tradeName: Hostsahring eG + +familyName + +givenName + } + + class partner-MeierGmbH { + +Numeric partnerNumber: 12345 + +Role partnerRole + } + partner-MeierGmbH o-- role-MeierGmbH class person-MeierGmbH { +personType: LEGAL @@ -36,17 +74,15 @@ classDiagram +familyName +givenName } - person-MeierGmbH *-- personDetails-MeierGmbH : optiona + person-MeierGmbH *-- personDetails-MeierGmbH class personDetails-MeierGmbH { - +personContact +registrationOffice: AG Hamburg +registrationNumber: ABC123434 +birthName +birthPlace +dateOfDeath } - personDetails-MeierGmbH o-- contactData-MeierGmbH class contactData-MeierGmbH { +postalAddress: Hauptstraße 5, 22345 Hamburg @@ -54,6 +90,18 @@ classDiagram +emailAddresses: office@meier-gmbh.de } + class role-MeierGmbH { + +RoleType RoleType PARTNER + +Person anchor + +Person holder + +Contact roleContact + } + role-MeierGmbH o-- person-HostsharingEG : anchor + role-MeierGmbH o-- person-MeierGmbH : holder + role-MeierGmbH o-- contactData-MeierGmbH + + %% --- Debitors --- + class debitor-MeierGmbH { +Partner partner +Numeric[2] debitorNumberSuffix: 00 @@ -85,6 +133,8 @@ classDiagram role-MeierGmbH-Buha o-- person-MeierGmbH : holder role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha + %% --- Representatives --- + class person-FrankMeier { + personType: NATURAL + tradeName @@ -108,6 +158,8 @@ classDiagram role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder role-MeierGmbH-FrankMeier o-- contactData-FrankMeier + %% --- Operations --- + class person-SabineMeier { +personType: NATURAL +tradeName @@ -131,25 +183,4 @@ classDiagram role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder role-MeierGmbH-SabineMeier o-- contactData-SabineMeier - namespace Enums { - - class RoleType { - <> - UNKNOWN - REPRESENTATIVE - ACCOUNTING - OPERATIONS - } - - class PersonType { - <> - UNKNOWN: nur für Import - NATURAL_PERSON: natürliche Person - LEGAL_PERSON: z.B. GmbH, e.K., eG, e.V. - INCORORATED_FIRM: z.B. OHG, Partnerschaftsgesellschaft - UNINCORPORATED_FIRM: z.B. GbR, ARGE, Erbengemeinschaft - PUBLIC_INSTITUTION: KdöR, AöR [ohne Registergericht/Registernummer] - } - } - ``` -- 2.39.2 From 52d4b4d4587e753c816bda496db33b064ec7bfdf Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 20 Jan 2024 14:46:35 +0100 Subject: [PATCH 55/72] add refundBankAccount to DebitorEntityPatcher --- .../debitor/HsOfficeDebitorEntityPatcher.java | 5 ++++ .../HsOfficeDebitorEntityPatcherUnitTest.java | 28 +++++++++++++++++-- .../hostsharing/test/PatchUnitTestBase.java | 3 +- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java index 092a0a9b..914c8230 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcher.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.office.debitor; +import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.mapper.EntityPatcher; @@ -35,6 +36,10 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher { + verifyNotNull(newValue, "refundBankAccount"); + entity.setRefundBankAccount(em.getReference(HsOfficeBankAccountEntity.class, newValue)); + }); } private void verifyNotNull(final Object newValue, final String propertyName) { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java index d94e9323..1983df59 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.office.debitor; +import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; @@ -42,6 +43,9 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< private static final boolean INITIAL_VAT_REVERSE_CHARGE = true; private static final boolean PATCHED_VAT_REVERSE_CHARGE = false; + private static final UUID INITIAL_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID(); + private static final UUID PATCHED_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID(); + private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder() .uuid(INITIAL_PARTNER_UUID) .build(); @@ -49,6 +53,10 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder() .uuid(INITIAL_CONTACT_UUID) .build(); + + private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder() + .uuid(INITIAL_REFUND_BANK_ACCOUNT_UUID) + .build(); @Mock private EntityManager em; @@ -56,8 +64,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< void initMocks() { lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); - lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> - HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); + lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation -> + HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build()); } @Override @@ -72,6 +80,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< entity.setVatBusiness(true); entity.setVatReverseCharge(INITIAL_VAT_REVERSE_CHARGE); entity.setDefaultPrefix("abc"); + entity.setRefundBankAccount(givenInitialBankAccount); return entity; } @@ -120,7 +129,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< new SimpleProperty<>( "personType", HsOfficeDebitorPatchResource::setVatBusiness, - PATCHED_BILLABLE, + PATCHED_VAT_BUSINESS, HsOfficeDebitorEntity::setVatBusiness) .notNullable(), new SimpleProperty<>( @@ -134,6 +143,13 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorPatchResource::setDefaultPrefix, PATCHED_DEFAULT_PREFIX, HsOfficeDebitorEntity::setDefaultPrefix) + .notNullable(), + new JsonNullableProperty<>( + "refundBankAccount", + HsOfficeDebitorPatchResource::setRefundBankAccountUuid, + PATCHED_REFUND_BANK_ACCOUNT_UUID, + HsOfficeDebitorEntity::setRefundBankAccount, + newBankAccount(PATCHED_REFUND_BANK_ACCOUNT_UUID)) .notNullable() ); } @@ -143,4 +159,10 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< newContact.setUuid(uuid); return newContact; } + + private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) { + final var newBankAccount = new HsOfficeBankAccountEntity(); + newBankAccount.setUuid(uuid); + return newBankAccount; + } } diff --git a/src/test/java/net/hostsharing/test/PatchUnitTestBase.java b/src/test/java/net/hostsharing/test/PatchUnitTestBase.java index 7be61b19..51f78bb4 100644 --- a/src/test/java/net/hostsharing/test/PatchUnitTestBase.java +++ b/src/test/java/net/hostsharing/test/PatchUnitTestBase.java @@ -1,5 +1,6 @@ package net.hostsharing.test; +import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.mapper.EntityPatcher; import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Test; @@ -232,7 +233,7 @@ public abstract class PatchUnitTestBase { } } - protected static class JsonNullableProperty extends Property { + protected static class JsonNullableProperty extends Property { private final BiConsumer> resourceSetter; public final RV givenPatchValue; -- 2.39.2 From fe4b0866ed708c04c669d1b4469cdff1226e55ae Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 20 Jan 2024 14:46:42 +0100 Subject: [PATCH 56/72] improved aliases --- .aliases | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.aliases b/.aliases index 948a58bb..b89f0e83 100644 --- a/.aliases +++ b/.aliases @@ -75,5 +75,5 @@ alias pg-sql-restore='gunzip --stdout | docker exec -i hsadmin-ng-postgres psql alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l' -alias spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' - +alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' +alias gw-test='. .aliases; ./gradlew test' -- 2.39.2 From b00c4ce8a85e5506935ea20a8a15a57a48cc8c40 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 20 Jan 2024 15:09:31 +0100 Subject: [PATCH 57/72] reactivate uncommented test and remove commented code --- .../db/changelog/270-hs-office-debitor.sql | 2 +- .../278-hs-office-debitor-test-data.sql | 1 - .../308-hs-office-membership-test-data.sql | 12 ------ ...fficeDebitorRepositoryIntegrationTest.java | 41 +++++++++++++------ 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/main/resources/db/changelog/270-hs-office-debitor.sql b/src/main/resources/db/changelog/270-hs-office-debitor.sql index c11ab269..a4f9178f 100644 --- a/src/main/resources/db/changelog/270-hs-office-debitor.sql +++ b/src/main/resources/db/changelog/270-hs-office-debitor.sql @@ -17,7 +17,7 @@ create table hs_office_debitor vatReverseCharge boolean not null, refundBankAccountUuid uuid references hs_office_bankaccount(uuid), defaultPrefix char(3) not null - constraint check_member_code check ( + constraint check_default_prefix check ( defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$' ) -- TODO.impl: SEPA-mandate diff --git a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql index 74cefde0..af75d074 100644 --- a/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql +++ b/src/main/resources/db/changelog/278-hs-office-debitor-test-data.sql @@ -33,7 +33,6 @@ begin select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact; select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid; - -- raise notice 'creating test debitor: % (#%)', idName, relatedPartner.debitorNumberPrefix || '00'; raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact; diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql index 1684ff09..5a4adaa6 100644 --- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql +++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql @@ -8,14 +8,11 @@ /* Creates a single membership test record. */ --- create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber integer ) create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric ) language plpgsql as $$ declare currentTask varchar; idName varchar; --- forDebitorNumberPrefix integer; --- forDebitorNumberSuffix integer; relatedPartner hs_office_partner; relatedDebitor hs_office_debitor; newMemberNumber numeric; @@ -28,9 +25,6 @@ begin select partner.* from hs_office_partner partner join hs_office_person person on person.uuid = partner.personUuid where person.tradeName = forPartnerTradeName into relatedPartner; --- forDebitorNumberPrefix := forMainDebitorNumber/ 100; --- forDebitorNumberSuffix := mod(forMainDebitorNumber, 100); --- select d.* from hs_office_debitor d where d.debitorNumberSuffix = forDebitorNumberSuffix into relatedDebitor; select d.* from hs_office_debitor d where d.debitorNumberSuffix = forMainDebitorNumber into relatedDebitor; select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber; @@ -39,7 +33,6 @@ begin raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; insert into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination) - -- values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, forDebitorNumberPrefix, daterange('20221001' , null, '[]'), 'NONE'); values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE'); end; $$; --// @@ -55,10 +48,5 @@ do language plpgsql $$ call createHsOfficeMembershipTestData('Second e.K.', 12); call createHsOfficeMembershipTestData('Third OHG', 13); end; --- begin --- call createHsOfficeMembershipTestData('First GmbH', 1000100); --- call createHsOfficeMembershipTestData('Second e.K.', 1000200); --- call createHsOfficeMembershipTestData('Third OHG', 1000300); --- end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index 2b12cffe..8cddbcd8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -20,14 +20,13 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.transaction.annotation.Transactional; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; @@ -65,8 +64,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { @MockBean HttpServletRequest request; - Set tempDebitors = new HashSet<>(); - @Nested class CreateDebitor { @@ -97,6 +94,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { assertThat(debitorRepo.count()).isEqualTo(count + 1); } + @ParameterizedTest + @ValueSource(strings = {"", "a", "ab", "a12", "123", "12a"}) + @Transactional + public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) { + // given + context("superuser-alex@hostsharing.net"); + final var count = debitorRepo.count(); + final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); + final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); + + // when + final var result = attempt(em, () -> { + final var newDebitor = HsOfficeDebitorEntity.builder() + .debitorNumberSuffix((byte)21) + .partner(givenPartner) + .billingContact(givenContact) + .billable(true) + .vatReverseCharge(false) + .vatBusiness(false) + .defaultPrefix(givenPrefix) + .build(); + return debitorRepo.save(newDebitor); + }); + + // then + result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class); + } + @Test public void createsAndGrantsRoles() { // given @@ -564,14 +589,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { }).assertSuccessful().returnedValue(); } - @BeforeEach - @AfterEach - void cleanup() { - context("superuser-alex@hostsharing.net"); - // TODO em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20").executeUpdate(); - em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20000").executeUpdate(); - } - void exactlyTheseDebitorsAreReturned(final List actualResult, final String... debitorNames) { assertThat(actualResult) .extracting(HsOfficeDebitorEntity::toString) -- 2.39.2 From 2a1e933c00482bb5eeec76cd72e04ec9f5b1659e Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Sat, 20 Jan 2024 15:33:25 +0100 Subject: [PATCH 58/72] remove commented code and other cleanup from code-review --- .../HsOfficeDebitorRepositoryIntegrationTest.java | 2 -- .../HsOfficeMembershipControllerAcceptanceTest.java | 4 ++-- ...HsOfficeMembershipRepositoryIntegrationTest.java | 3 --- .../hs/office/migration/ImportOfficeData.java | 13 ------------- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index 8cddbcd8..e140db21 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -9,8 +9,6 @@ import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository; import net.hostsharing.test.Array; import net.hostsharing.test.JpaAttempt; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 243a6fe2..6d394b53 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -143,7 +143,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .contentType(ContentType.JSON) .body("uuid", isUuidValid()) .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) -// .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumberSuffix())) + .body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix())) .body("partner.person.tradeName", is("Third OHG")) .body("memberNumber", is(20001)) .body("validFrom", is("2022-10-13")) @@ -278,7 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("uuid", isUuidValid()) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber())) -// .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumberSuffix())) + .body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix())) .body("memberNumber", is(givenMembership.getMemberNumber())) .body("validFrom", is("2022-11-01")) .body("validTo", is("2023-12-31")) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index c6de2a44..d633d8af 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -150,8 +150,6 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // tenant "{ grant role membership#20002:First.tenant to role membership#20002:First.agent by system and assume }", "{ grant role partner#10001:First.guest to role membership#20002:First.tenant by system and assume }", -// "{ grant role debitor#1100First.guest to role membership#20002:First.tenant by system and assume }", -// "{ grant role membership#20002:First.tenant to role debitor#1100First.agent by system and assume }", "{ grant role debitor#1000111:First.guest to role membership#20002:First.tenant by system and assume }", "{ grant role membership#20002:First.tenant to role debitor#1000111:First.agent by system and assume }", @@ -161,7 +159,6 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { "{ grant perm view on membership#20002:First to role membership#20002:First.guest by system and assume }", "{ grant role membership#20002:First.guest to role membership#20002:First.tenant by system and assume }", "{ grant role membership#20002:First.guest to role partner#10001:First.tenant by system and assume }", -// "{ grant role membership#20002:First.guest to role debitor#1100First.tenant by system and assume }", "{ grant role membership#20002:First.guest to role debitor#1000111:First.tenant by system and assume }", null)); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 7bdf815b..50c07634 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -459,19 +459,6 @@ public class ImportOfficeData extends ContextBasedTest { private void deleteFromRbacTables() { jpaAttempt.transacted(() -> { context(rbacSuperuser); - final var usersNotToDelete = em.createNativeQuery("select name from rbacuser where name like 'superuser-%'") - .getResultList(); - final var usersToDelete = em.createNativeQuery("select name from rbacuser where name not like 'superuser-%'") - .getResultList(); - System.getenv(); - }); - - jpaAttempt.transacted(() -> { - context(rbacSuperuser); - // em.createNativeQuery("DELETE FROM rbacobject WHERE objecttable like 'hs_%'").executeUpdate(); - // em.createNativeQuery("DELETE FROM rbacgrants WHERE true").executeUpdate(); - // em.createNativeQuery("DELETE FROM rbacpermission WHERE true").executeUpdate(); - // em.createNativeQuery("DELETE FROM rbacreference WHERE true").executeUpdate(); em.createNativeQuery("delete from rbacuser_rv where name not like 'superuser-%'").executeUpdate(); em.createNativeQuery("delete from tx_journal where true").executeUpdate(); em.createNativeQuery("delete from tx_context where true").executeUpdate(); -- 2.39.2 From 678c79cf4f6d94b0ba118b13e26bcb1f89166196 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 22 Jan 2024 10:36:32 +0100 Subject: [PATCH 59/72] ignore .environment* --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 10ebda13..d6a2e347 100644 --- a/.gitignore +++ b/.gitignore @@ -137,4 +137,4 @@ Desktop.ini # ESLint ###################### .eslintcache -/.environment +/.environment* -- 2.39.2 From 13ddaa2afdb5a7c073758ba54369c3ac4a118940 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 22 Jan 2024 12:33:28 +0100 Subject: [PATCH 60/72] separate actual import and verification test methods --- .aliases | 5 +- .../hs/office/migration/ImportOfficeData.java | 48 ++++++++++++++----- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/.aliases b/.aliases index b89f0e83..c0b4e22d 100644 --- a/.aliases +++ b/.aliases @@ -56,7 +56,10 @@ function importOfficeData() { source .environment fi - ./gradlew importOfficeData + echo "using environment (with ending ';' for use in IntelliJ IDEA):" + set | grep ^HSADMINNG_ | sed 's/$/;/' + + ./gradlew importOfficeData --rerun } alias gw-importOfficeData=importOfficeData diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 50c07634..fea66f1b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -34,6 +34,7 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.test.annotation.Commit; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.transaction.support.TransactionTemplate; import jakarta.persistence.EntityManager; @@ -106,6 +107,7 @@ import static org.assertj.core.api.Fail.fail; "spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}", "hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}" }) +@DirtiesContext @Import({ Context.class, JpaAttempt.class }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @ExtendWith(OrderedDependedTestsExtension.class) @@ -113,6 +115,9 @@ public class ImportOfficeData extends ContextBasedTest { static int relationshipId = 2000000; + @Value("${spring.datasource.url}") + private String jdbcUrl; + @Value("${spring.datasource.username}") private String postgresAdminUser; @@ -144,7 +149,7 @@ public class ImportOfficeData extends ContextBasedTest { HttpServletRequest request; @Test - @Order(1) + @Order(1010) void importBusinessPartners() { try (Reader reader = resourceReader("migration/business-partners.csv")) { @@ -153,7 +158,11 @@ public class ImportOfficeData extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + } + @Test + @Order(1011) + void verifyBusinessPartners() { assumeThat(postgresAdminUser).isEqualTo("admin"); // no contacts yet => mostly null values @@ -181,7 +190,7 @@ public class ImportOfficeData extends ContextBasedTest { } @Test - @Order(2) + @Order(1020) void importContacts() { try (Reader reader = resourceReader("migration/contacts.csv")) { @@ -190,7 +199,11 @@ public class ImportOfficeData extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + } + @Test + @Order(1021) + void verifyContacts() { assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" @@ -249,7 +262,7 @@ public class ImportOfficeData extends ContextBasedTest { } @Test - @Order(3) + @Order(1030) void importSepaMandates() { try (Reader reader = resourceReader("migration/sepa-mandates.csv")) { @@ -258,7 +271,11 @@ public class ImportOfficeData extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + } + @Test + @Order(1031) + void verifySepaMandates() { assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace(""" @@ -278,8 +295,9 @@ public class ImportOfficeData extends ContextBasedTest { } @Test - @Order(4) + @Order(1040) void importCoopShares() { + assumeThat(postgresAdminUser).isEqualTo("admin"); try (Reader reader = resourceReader("migration/share-transactions.csv")) { final var lines = readAllLines(reader); @@ -287,7 +305,11 @@ public class ImportOfficeData extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + } + @Test + @Order(1041) + void verifyCoopShares() { assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(coopShares.toString()).isEqualToIgnoringWhitespace(""" @@ -301,7 +323,7 @@ public class ImportOfficeData extends ContextBasedTest { } @Test - @Order(5) + @Order(1050) void importCoopAssets() { try (Reader reader = resourceReader("migration/asset-transactions.csv")) { @@ -310,7 +332,11 @@ public class ImportOfficeData extends ContextBasedTest { } catch (Exception e) { throw new RuntimeException(e); } + } + @Test + @Order(1051) + void verifyCoopAssets() { assumeThat(postgresAdminUser).isEqualTo("admin"); assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace(""" @@ -328,10 +354,11 @@ public class ImportOfficeData extends ContextBasedTest { } @Test - @Order(10) + @Order(2000) @Commit void persistEntities() { + System.out.println("PERSISTING to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); deleteTestDataFromHsOfficeTables(); resetFromHsOfficeSequences(); deleteFromTestTables(); @@ -999,16 +1026,15 @@ class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { private static boolean previousTestsPassed = true; - public void testAborted(ExtensionContext context, Throwable cause) { - previousTestsPassed = false; - } - public void testFailed(ExtensionContext context, Throwable cause) { previousTestsPassed = false; } @Override - public void beforeEach(final ExtensionContext extensionContext) throws Exception { + public void beforeEach(final ExtensionContext extensionContext) { + if (!previousTestsPassed) { + System.err.println("ignoring because previous fest has failed"); + } assumeThat(previousTestsPassed).isTrue(); } } -- 2.39.2 From 9f9575f51f8af3e24a573b76487a8b49e0d72bca Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 22 Jan 2024 10:48:39 +0100 Subject: [PATCH 61/72] rename debitorNumberPrefix -> partnerNumber --- .../hsadminng/hs/office/debitor/HsOfficeDebitorEntity.java | 6 +++--- .../hs/office/debitor/HsOfficeDebitorRepository.java | 4 ++-- .../hsadminng/hs/office/partner/HsOfficePartnerEntity.java | 4 ++-- .../api-definition/hs-office/hs-office-partner-schemas.yaml | 6 +++--- src/main/resources/db/changelog/220-hs-office-partner.sql | 2 +- .../resources/db/changelog/223-hs-office-partner-rbac.sql | 6 +++--- .../db/changelog/228-hs-office-partner-test-data.sql | 6 +++--- .../resources/db/changelog/273-hs-office-debitor-rbac.sql | 2 +- .../hs/office/debitor/HsOfficeDebitorEntityUnitTest.java | 6 +++--- .../hsadminng/hs/office/migration/ImportOfficeData.java | 2 +- .../partner/HsOfficePartnerControllerAcceptanceTest.java | 6 +++--- .../partner/HsOfficePartnerRepositoryIntegrationTest.java | 6 +++--- .../hsadminng/hs/office/partner/TestHsOfficePartner.java | 2 +- 13 files changed, 29 insertions(+), 29 deletions(-) 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 332ea8dd..74787b0d 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 @@ -77,16 +77,16 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { public String getDebitorNumberString() { // TODO: refactor - if (partner.getDebitorNumberPrefix() == null ) { + if (partner.getPartnerNumber() == null ) { if (debitorNumberSuffix == null) { return null; } return String.format("%02d", debitorNumberSuffix); } if (debitorNumberSuffix == null) { - return partner.getDebitorNumberPrefix() + "??"; + return partner.getPartnerNumber() + "??"; } - return partner.getDebitorNumberPrefix() + String.format("%02d", debitorNumberSuffix); + return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix); } public Integer getDebitorNumber() { diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java index 5ca04719..64be98b1 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepository.java @@ -13,10 +13,10 @@ public interface HsOfficeDebitorRepository extends Repository findDebitorByDebitorNumber(int debitorNumberPrefix, byte debitorNumberSuffix); + List findDebitorByDebitorNumber(int partnerNumber, byte debitorNumberSuffix); default List findDebitorByDebitorNumber(int debitorNumber) { return findDebitorByDebitorNumber( debitorNumber/100, (byte) (debitorNumber%100)); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java index 95a30290..850b94db 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerEntity.java @@ -36,8 +36,8 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid { @GeneratedValue private UUID uuid; - @Column(name = "debitornumberprefix", columnDefinition = "numeric(5) not null") - private Integer debitorNumberPrefix; + @Column(name = "partnernumber", columnDefinition = "numeric(5) not null") + private Integer partnerNumber; @ManyToOne @JoinColumn(name = "personuuid", nullable = false) diff --git a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml index 6b044a27..a6a94f67 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-partner-schemas.yaml @@ -9,7 +9,7 @@ components: uuid: type: string format: uuid - debitorNumberPrefix: + partnerNumber: type: integer format: int8 minimum: 10000 @@ -91,7 +91,7 @@ components: HsOfficePartnerInsert: type: object properties: - debitorNumberPrefix: + partnerNumber: type: integer format: int8 minimum: 10000 @@ -105,7 +105,7 @@ components: details: $ref: '#/components/schemas/HsOfficePartnerDetailsInsert' required: - - debitorNumberPrefix + - partnerNumber - personUuid - contactUuid - details diff --git a/src/main/resources/db/changelog/220-hs-office-partner.sql b/src/main/resources/db/changelog/220-hs-office-partner.sql index 340c0455..67126766 100644 --- a/src/main/resources/db/changelog/220-hs-office-partner.sql +++ b/src/main/resources/db/changelog/220-hs-office-partner.sql @@ -32,7 +32,7 @@ call create_journal('hs_office_partner_details'); create table hs_office_partner ( uuid uuid unique references RbacObject (uuid) initially deferred, - debitorNumberPrefix varchar(5), + partnerNumber varchar(5), personUuid uuid not null references hs_office_person(uuid), contactUuid uuid not null references hs_office_contact(uuid), detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade diff --git a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql index db5db365..852ed8ad 100644 --- a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql @@ -165,8 +165,8 @@ execute procedure hsOfficePartnerRbacRolesTrigger(); --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_partner', $idName$ - -- TODO: simplify by using just debitorNumberPrefix for the essential part - debitorNumberPrefix || ':' || + -- TODO: simplify by using just partnerNumber for the essential part + partnerNumber || ':' || (select idName from hs_office_person_iv p where p.uuid = target.personuuid) || '-' || (select idName from hs_office_contact_iv c where c.uuid = target.contactuuid) @@ -180,7 +180,7 @@ call generateRbacIdentityView('hs_office_partner', $idName$ call generateRbacRestrictedView('hs_office_partner', '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', $updates$ - debitorNumberPrefix = new.debitorNumberPrefix, + partnerNumber = new.partnerNumber, personUuid = new.personUuid, contactUuid = new.contactUuid $updates$); diff --git a/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql b/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql index 759c48c9..64df0695 100644 --- a/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql +++ b/src/main/resources/db/changelog/228-hs-office-partner-test-data.sql @@ -9,7 +9,7 @@ Creates a single partner test record. */ create or replace procedure createHsOfficePartnerTestData( - debitorNumberPrefix numeric(5), + partnerNumber numeric(5), personTradeOrFamilyName varchar, contactLabel varchar ) language plpgsql as $$ @@ -54,8 +54,8 @@ begin end if; insert - into hs_office_partner (uuid, debitorNumberPrefix, personuuid, contactuuid, detailsUuid) - values (uuid_generate_v4(), debitorNumberPrefix, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); + into hs_office_partner (uuid, partnerNumber, personuuid, contactuuid, detailsUuid) + values (uuid_generate_v4(), partnerNumber, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); end; $$; --// diff --git a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql index 78daaea5..ad12e713 100644 --- a/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql +++ b/src/main/resources/db/changelog/273-hs-office-debitor-rbac.sql @@ -173,7 +173,7 @@ execute procedure hsOfficeDebitorRbacRolesTrigger(); -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_debitor', $idName$ '#' || - (select debitornumberprefix from hs_office_partner p where p.uuid = target.partnerUuid) || + (select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) || to_char(debitorNumberSuffix, 'fm00') || ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid) $idName$); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java index 270431b9..c1cb6c18 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java @@ -21,7 +21,7 @@ class HsOfficeDebitorEntityUnitTest { .tradeName("some trade name") .build()) .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) - .debitorNumberPrefix(12345) + .partnerNumber(12345) .build()) .billingContact(HsOfficeContactEntity.builder().label("some label").build()) .defaultPrefix("som") @@ -39,7 +39,7 @@ class HsOfficeDebitorEntityUnitTest { .partner(HsOfficePartnerEntity.builder() .person(null) .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) - .debitorNumberPrefix(12345) + .partnerNumber(12345) .build()) .billingContact(HsOfficeContactEntity.builder().label("some label").build()) .build(); @@ -53,7 +53,7 @@ class HsOfficeDebitorEntityUnitTest { void toShortStringContainsDebitorNumber() { final var given = HsOfficeDebitorEntity.builder() .partner(HsOfficePartnerEntity.builder() - .debitorNumberPrefix(12345) + .partnerNumber(12345) .build()) .debitorNumberSuffix((byte)67) .build(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index fea66f1b..a711aed0 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -551,7 +551,7 @@ public class ImportOfficeData extends ContextBasedTest { final var person = HsOfficePersonEntity.builder().build(); final var partner = HsOfficePartnerEntity.builder() - .debitorNumberPrefix(rec.getInteger("member_id")) + .partnerNumber(rec.getInteger("member_id")) .details(HsOfficePartnerDetailsEntity.builder().build()) .contact(null) // is set during contacts import depending on assigned roles .person(person) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java index 0ac1eb2d..61196cae 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerControllerAcceptanceTest.java @@ -125,7 +125,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { - "debitorNumberPrefix": "12345", + "partnerNumber": "12345", "contactUuid": "%s", "personUuid": "%s", "details": { @@ -167,7 +167,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { - "debitorNumberPrefix": "12345", + "partnerNumber": "12345", "contactUuid": "%s", "personUuid": "%s", "details": {} @@ -195,7 +195,7 @@ class HsOfficePartnerControllerAcceptanceTest { .contentType(ContentType.JSON) .body(""" { - "debitorNumberPrefix": "12345", + "partnerNumber": "12345", "contactUuid": "%s", "personUuid": "%s", "details": {} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index ad41e939..18eb063d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -105,7 +105,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var newPartner = toCleanup(HsOfficePartnerEntity.builder() - .debitorNumberPrefix(22222) + .partnerNumber(22222) .person(givenPerson) .contact(givenContact) .details(HsOfficePartnerDetailsEntity.builder().build()) @@ -396,13 +396,13 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { } private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler( - final Integer debitorNumberPrefix, final String person, final String contact) { + final Integer partnerNumber, final String person, final String contact) { return jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var newPartner = HsOfficePartnerEntity.builder() - .debitorNumberPrefix(debitorNumberPrefix) + .partnerNumber(partnerNumber) .person(givenPerson) .contact(givenContact) .details(HsOfficePartnerDetailsEntity.builder().build()) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java index 8ac5ae85..316496bc 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/TestHsOfficePartner.java @@ -12,7 +12,7 @@ public class TestHsOfficePartner { static public HsOfficePartnerEntity hsOfficePartnerWithLegalPerson(final String tradeName) { return HsOfficePartnerEntity.builder() - .debitorNumberPrefix(10001) + .partnerNumber(10001) .person(HsOfficePersonEntity.builder() .personType(LEGAL) .tradeName(tradeName) -- 2.39.2 From 3c89164a67b2fc1bbb834bb33033f433bb765329 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Mon, 22 Jan 2024 13:15:20 +0100 Subject: [PATCH 62/72] rename membernumber -> membernumbersuffix --- .../HsOfficeCoopAssetsTransactionEntity.java | 4 +-- ...OfficeCoopAssetsTransactionRepository.java | 2 +- .../HsOfficeCoopSharesTransactionEntity.java | 4 +-- ...OfficeCoopSharesTransactionRepository.java | 2 +- .../HsOfficeMembershipController.java | 4 +-- .../membership/HsOfficeMembershipEntity.java | 8 ++--- .../HsOfficeMembershipRepository.java | 6 ++-- .../hs-office-membership-schemas.yaml | 4 +-- .../hs-office/hs-office-memberships.yaml | 2 +- .../db/changelog/300-hs-office-membership.sql | 2 +- .../303-hs-office-membership-rbac.sql | 4 +-- .../308-hs-office-membership-test-data.sql | 8 ++--- .../318-hs-office-coopshares-test-data.sql | 2 +- .../328-hs-office-coopassets-test-data.sql | 2 +- ...tsTransactionControllerAcceptanceTest.java | 12 +++---- ...ceCoopAssetsTransactionEntityUnitTest.java | 2 +- ...sTransactionRepositoryIntegrationTest.java | 8 ++--- ...esTransactionControllerAcceptanceTest.java | 12 +++---- ...ceCoopSharesTransactionEntityUnitTest.java | 2 +- ...sTransactionRepositoryIntegrationTest.java | 8 ++--- ...iceMembershipControllerAcceptanceTest.java | 34 +++++++++---------- .../HsOfficeMembershipControllerRestTest.java | 8 ++--- .../HsOfficeMembershipEntityUnitTest.java | 4 +-- ...ceMembershipRepositoryIntegrationTest.java | 16 ++++----- .../office/membership/TestHsMembership.java | 2 +- .../hs/office/migration/ImportOfficeData.java | 2 +- 26 files changed, 82 insertions(+), 82 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index ffc60bd1..41e5710f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -27,7 +27,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class) - .withProp(e -> e.getMembership().getMemberNumber()) + .withProp(e -> e.getMembership().getMemberNumberSuffix()) .withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate) .withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType) .withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue) @@ -67,6 +67,6 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu @Override public String toShortString() { - return membership.getMemberNumber() + new DecimalFormat("+0.00").format(assetValue); + return membership.getMemberNumberSuffix() + new DecimalFormat("+0.00").format(assetValue); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java index 256933b9..c606f476 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepository.java @@ -17,7 +17,7 @@ public interface HsOfficeCoopAssetsTransactionRepository extends Repository= :fromValueDate)) AND ( CAST(:toValueDate AS java.time.LocalDate)IS NULL OR (at.valueDate <= :toValueDate)) - ORDER BY at.membership.memberNumber, at.valueDate + ORDER BY at.membership.memberNumberSuffix, at.valueDate """) List findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( UUID membershipUuid, LocalDate fromValueDate, LocalDate toValueDate); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index a9fd4bde..bf52837f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -24,7 +24,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeCoopSharesTransactionEntity.class) - .withProp(e -> e.getMembership().getMemberNumber()) + .withProp(e -> e.getMembership().getMemberNumberSuffix()) .withProp(HsOfficeCoopSharesTransactionEntity::getValueDate) .withProp(HsOfficeCoopSharesTransactionEntity::getTransactionType) .withProp(HsOfficeCoopSharesTransactionEntity::getShareCount) @@ -63,6 +63,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu @Override public String toShortString() { - return "%s%+d".formatted(membership.getMemberNumber(), shareCount); + return "%s%+d".formatted(membership.getMemberNumberSuffix(), shareCount); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java index 2e52b828..5c3e0af6 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepository.java @@ -17,7 +17,7 @@ public interface HsOfficeCoopSharesTransactionRepository extends Repository= :fromValueDate)) AND ( CAST(:toValueDate AS java.time.LocalDate)IS NULL OR (st.valueDate <= :toValueDate)) - ORDER BY st.membership.memberNumber, st.valueDate + ORDER BY st.membership.memberNumberSuffix, st.valueDate """) List findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( UUID membershipUuid, LocalDate fromValueDate, LocalDate toValueDate); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index 3553f616..b46b02a8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -41,11 +41,11 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { final String currentUser, final String assumedRoles, UUID partnerUuid, - Integer memberNumber) { + Integer memberNumberSuffix) { context.define(currentUser, assumedRoles); final var entities = - membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(partnerUuid, memberNumber); + membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(partnerUuid, memberNumberSuffix); final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); 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 b5846324..5991a9ac 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 @@ -31,7 +31,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { private static Stringify stringify = stringify(HsOfficeMembershipEntity.class) - .withProp(HsOfficeMembershipEntity::getMemberNumber) + .withProp(HsOfficeMembershipEntity::getMemberNumberSuffix) .withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getMainDebitor().toShortString()) .withProp(e -> e.getValidity().asString()) @@ -52,8 +52,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @JoinColumn(name = "maindebitoruuid") private HsOfficeDebitorEntity mainDebitor; - @Column(name = "membernumber") - private int memberNumber; // TODO: migrate to suffix, like debitorNumberSuffix + @Column(name = "membernumbersuffix") + private int memberNumberSuffix; @Column(name = "validity", columnDefinition = "daterange") @Type(PostgreSQLRangeType.class) @@ -98,7 +98,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return String.valueOf(memberNumber); + return String.valueOf(memberNumberSuffix); } @PrePersist diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index 972c00ce..1bee3f73 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -13,12 +13,12 @@ public interface HsOfficeMembershipRepository extends Repository findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(UUID partnerUuid, Integer memberNumber); + List findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(UUID partnerUuid, Integer memberNumberSuffix); HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity); diff --git a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml index 74c8143b..855988b0 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml @@ -23,7 +23,7 @@ components: $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' mainDebitor: $ref: './hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor' - memberNumber: + memberNumberSuffix: type: integer validFrom: type: string @@ -66,7 +66,7 @@ components: type: string format: uuid nullable: false - memberNumber: + memberNumberSuffix: type: integer nullable: false validFrom: diff --git a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml index d4d60158..c18ba861 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml @@ -14,7 +14,7 @@ get: type: string format: uuid description: UUID of the business partner. - - name: memberNumber + - name: memberNumberSuffix in: query required: false schema: diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index 12f2dd34..750fd680 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -13,7 +13,7 @@ create table if not exists hs_office_membership uuid uuid unique references RbacObject (uuid) initially deferred, partnerUuid uuid not null references hs_office_partner(uuid), mainDebitorUuid uuid not null references hs_office_debitor(uuid), - memberNumber numeric(5) not null unique, + memberNumberSuffix numeric(5) not null unique, validity daterange not null, reasonForTermination HsOfficeReasonForTermination not null default 'NONE', membershipFeeBillable boolean not null default true diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index 972021e7..cc7deb48 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -92,7 +92,7 @@ execute procedure hsOfficeMembershipRbacRolesTrigger(); --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$ - target.memberNumber || + target.memberNumberSuffix || ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid) $idName$); --// @@ -102,7 +102,7 @@ call generateRbacIdentityView('hs_office_membership', idNameExpression => $idNam --changeset hs-office-membership-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacRestrictedView('hs_office_membership', - orderby => 'target.memberNumber', + orderby => 'target.memberNumberSuffix', columnUpdates => $updates$ validity = new.validity, reasonForTermination = new.reasonForTermination, diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql index 5a4adaa6..6d182650 100644 --- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql +++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql @@ -15,7 +15,7 @@ declare idName varchar; relatedPartner hs_office_partner; relatedDebitor hs_office_debitor; - newMemberNumber numeric; + newMemberNumberSuffix numeric; begin idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumber); currentTask := 'creating Membership test-data ' || idName; @@ -26,14 +26,14 @@ begin join hs_office_person person on person.uuid = partner.personUuid where person.tradeName = forPartnerTradeName into relatedPartner; select d.* from hs_office_debitor d where d.debitorNumberSuffix = forMainDebitorNumber into relatedDebitor; - select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber; + select coalesce(max(memberNumberSuffix)+1, 10001) from hs_office_membership into newMemberNumberSuffix; raise notice 'creating test Membership: %', idName; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; insert - into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination) - values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE'); + into hs_office_membership (uuid, partneruuid, maindebitoruuid, memberNumberSuffix, validity, reasonfortermination) + values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE'); end; $$; --// diff --git a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql index c9252568..987f0aba 100644 --- a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql +++ b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql @@ -18,7 +18,7 @@ begin execute format('set local hsadminng.currentTask to %L', currentTask); call defineContext(currentTask); - select m.uuid from hs_office_membership m where m.memberNumber = givenMembershipNumber into membership; + select m.uuid from hs_office_membership m where m.memberNumberSuffix = givenMembershipNumber into membership; raise notice 'creating test coopSharesTransaction: %', givenMembershipNumber; insert diff --git a/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql b/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql index 8259a7e3..4589ceb6 100644 --- a/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql +++ b/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql @@ -18,7 +18,7 @@ begin execute format('set local hsadminng.currentTask to %L', currentTask); call defineContext(currentTask); - select m.uuid from hs_office_membership m where m.memberNumber = givenMembershipNumber into membership; + select m.uuid from hs_office_membership m where m.memberNumberSuffix = givenMembershipNumber into membership; raise notice 'creating test coopAssetsTransaction: %', givenMembershipNumber; insert diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index 292a947c..edf0eed3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -72,10 +72,10 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopAssetsTransactionsByMemberNumber() { + void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); RestAssured // @formatter:off @@ -115,10 +115,10 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberAndDateRange() { + void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); RestAssured // @formatter:off @@ -153,7 +153,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopAssetsTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) .get(0); final var location = RestAssured // @formatter:off @@ -199,7 +199,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) .get(0); final var location = RestAssured // @formatter:off diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java index 7be6ec0b..6371c816 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java @@ -26,7 +26,7 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest { } @Test - void toShortStringContainsOnlyMemberNumberAndSharesCountOnly() { + void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() { final var result = givenCoopAssetTransaction.toShortString(); assertThat(result).isEqualTo("300001+128.00"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index 851ccb69..bf9cc649 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -62,7 +62,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopAssetsTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) .get(0); // when @@ -96,7 +96,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( null, 10001).get(0); final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() @@ -161,7 +161,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); // when @@ -182,7 +182,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); // when diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index f3c6cc33..cd4aaffa 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -72,10 +72,10 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopSharesTransactionsByMemberNumber() { + void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002).get(0); + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002).get(0); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -106,10 +106,10 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopSharesTransactionsByMemberNumberAndDateRange() { + void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002).get(0); + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002).get(0); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -134,7 +134,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopSharesTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001).get(0); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" @@ -165,7 +165,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001).get(0); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java index fc0df074..48dbd672 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java @@ -25,7 +25,7 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { } @Test - void toShortStringContainsOnlyMemberNumberAndshareCountOnly() { + void toShortStringContainsOnlyMemberNumberSuffixAndshareCountOnly() { final var result = givenCoopSharesTransaction.toShortString(); assertThat(result).isEqualTo("300001+4"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index 8dee222f..8b0c607e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -61,7 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopSharesTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) .get(0); // when @@ -95,7 +95,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( null, 10001).get(0); final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder() @@ -160,7 +160,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); // when @@ -181,7 +181,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) .get(0); // when diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 6d394b53..1f83ae07 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -61,7 +61,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @PersistenceContext EntityManager em; - private static int tempMemberNumber = 20010; + private static int tempMemberNumberSuffix = 20010; @Nested @Accepts({ "Membership:F(Find)" }) @@ -84,7 +84,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "First GmbH" } }, "mainDebitor": { "debitorNumber": 1000111 }, - "memberNumber": 10001, + "memberNumberSuffix": 10001, "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -92,7 +92,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "Second e.K." } }, "mainDebitor": { "debitorNumber": 1000212 }, - "memberNumber": 10002, + "memberNumberSuffix": 10002, "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -100,7 +100,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "Third OHG" } }, "mainDebitor": { "debitorNumber": 1000313 }, - "memberNumber": 10003, + "memberNumberSuffix": 10003, "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -130,7 +130,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partnerUuid": "%s", "mainDebitorUuid": "%s", - "memberNumber": 20001, + "memberNumberSuffix": 20001, "validFrom": "2022-10-13", "membershipFeeBillable": "true" } @@ -145,7 +145,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) .body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix())) .body("partner.person.tradeName", is("Third OHG")) - .body("memberNumber", is(20001)) + .body("memberNumberSuffix", is(20001)) .body("validFrom", is("2022-10-13")) .body("validTo", equalTo(null)) .header("Location", startsWith("http://localhost")) @@ -166,7 +166,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Test void globalAdmin_canGetArbitraryMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( null, 10001) .get(0) @@ -185,7 +185,7 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "First GmbH" } }, "mainDebitor": { "debitorNumber": 1000111 }, - "memberNumber": 10001, + "memberNumberSuffix": 10001, "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -197,7 +197,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void normalUser_canNotGetUnrelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( null, 10001) .get(0) @@ -217,7 +217,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void debitorAgentUser_canGetRelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( null, 10003) .get(0) @@ -240,7 +240,7 @@ class HsOfficeMembershipControllerAcceptanceTest { "debitorNumber": 1000313, "billingContact": { "label": "third contact" } }, - "memberNumber": 10003, + "memberNumberSuffix": 10003, "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -279,7 +279,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber())) .body("mainDebitor.debitorNumberSuffix", is((int) givenMembership.getMainDebitor().getDebitorNumberSuffix())) - .body("memberNumber", is(givenMembership.getMemberNumber())) + .body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix())) .body("validFrom", is("2022-11-01")) .body("validTo", is("2023-12-31")) .body("reasonForTermination", is("CANCELLATION")); @@ -290,7 +290,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .matches(mandate -> { assertThat(mandate.getPartner().toShortString()).isEqualTo("LEGAL First GmbH"); assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString()); - assertThat(mandate.getMemberNumber()).isEqualTo(givenMembership.getMemberNumber()); + assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix()); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)"); assertThat(mandate.getReasonForTermination()).isEqualTo(HsOfficeReasonForTermination.CANCELLATION); return true; @@ -322,7 +322,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("uuid", isUuidValid()) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("mainDebitor.debitorNumber", is(1000313)) - .body("memberNumber", is(givenMembership.getMemberNumber())) + .body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix())) .body("validFrom", is("2022-11-01")) .body("validTo", nullValue()) .body("reasonForTermination", is("NONE")); @@ -333,7 +333,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .matches(mandate -> { assertThat(mandate.getPartner().toShortString()).isEqualTo("LEGAL First GmbH"); assertThat(mandate.getMainDebitor().toString()).isEqualTo(givenMembership.getMainDebitor().toString()); - assertThat(mandate.getMemberNumber()).isEqualTo(givenMembership.getMemberNumber()); + assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix()); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,)"); assertThat(mandate.getReasonForTermination()).isEqualTo(NONE); return true; @@ -444,7 +444,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .uuid(UUID.randomUUID()) .partner(givenPartner) .mainDebitor(givenDebitor) - .memberNumber(++tempMemberNumber) + .memberNumberSuffix(++tempMemberNumberSuffix) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .reasonForTermination(NONE) .membershipFeeBillable(true) @@ -459,7 +459,7 @@ class HsOfficeMembershipControllerAcceptanceTest { void cleanup() { jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net", null); - final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumber >= 20000"); + final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= 20000"); query.executeUpdate(); }); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 2f42cb58..30617a36 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -71,7 +71,7 @@ public class HsOfficeMembershipControllerRestTest { { "partnerUuid": null, "mainDebitorUuid": "%s", - "memberNumber": 20001, + "memberNumberSuffix": 20001, "validFrom": "2022-10-13", "membershipFeeBillable": "true" } @@ -97,7 +97,7 @@ public class HsOfficeMembershipControllerRestTest { { "partnerUuid": "%s", "mainDebitorUuid": null, - "memberNumber": 20001, + "memberNumberSuffix": 20001, "validFrom": "2022-10-13", "membershipFeeBillable": "true" } @@ -129,7 +129,7 @@ public class HsOfficeMembershipControllerRestTest { { "partnerUuid": "%s", "mainDebitorUuid": "%s", - "memberNumber": 20001, + "memberNumberSuffix": 20001, "validFrom": "2022-10-13", "membershipFeeBillable": "true" } @@ -161,7 +161,7 @@ public class HsOfficeMembershipControllerRestTest { { "partnerUuid": "%s", "mainDebitorUuid": "%s", - "memberNumber": 20001, + "memberNumberSuffix": 20001, "validFrom": "2022-10-13", "membershipFeeBillable": "true" } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index 14959428..5ba69bbc 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -17,7 +17,7 @@ class HsOfficeMembershipEntityUnitTest { public static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-01-01"); final HsOfficeMembershipEntity givenMembership = HsOfficeMembershipEntity.builder() - .memberNumber(10001) + .memberNumberSuffix(10001) .partner(TEST_PARTNER) .mainDebitor(TEST_DEBITOR) .validity(Range.closedInfinite(GIVEN_VALID_FROM)) @@ -31,7 +31,7 @@ class HsOfficeMembershipEntityUnitTest { } @Test - void toShortStringContainsMemberNumberOnly() { + void toShortStringContainsMemberNumberSuffixOnly() { final var result = givenMembership.toShortString(); assertThat(result).isEqualTo("10001"); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index d633d8af..c9a8eb0d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -77,7 +77,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // when final var result = attempt(em, () -> { final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumber(20001) + .memberNumberSuffix(20001) .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) @@ -108,7 +108,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumber(20002) + .memberNumberSuffix(20002) .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) @@ -179,7 +179,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, null); + final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, null); // then exactlyTheseMembershipsAreReturned( @@ -196,7 +196,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber( + final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( givenPartner.getUuid(), null); @@ -205,12 +205,12 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { } @Test - public void globalAdmin_withoutAssumedRole_canFindAllMembershipByMemberNumber() { + public void globalAdmin_withoutAssumedRole_canFindAllMembershipByMemberNumberSuffix() { // given context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002); + final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002); // then exactlyTheseMembershipsAreReturned(result, "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); @@ -401,7 +401,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { }); jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net", null); - em.createQuery("DELETE FROM HsOfficeMembershipEntity WHERE memberNumber >= 20000"); + em.createQuery("DELETE FROM HsOfficeMembershipEntity WHERE memberNumberSuffix >= 20000"); }); } @@ -411,7 +411,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerTradeName).get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike(debitorName).get(0); final var newMembership = HsOfficeMembershipEntity.builder() - .memberNumber(20002) + .memberNumberSuffix(20002) .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java index d9245fc8..3fc8c471 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java @@ -11,7 +11,7 @@ public class TestHsMembership { public static final HsOfficeMembershipEntity TEST_MEMBERSHIP = HsOfficeMembershipEntity.builder() .partner(TEST_PARTNER) - .memberNumber(300001) + .memberNumberSuffix(300001) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) .build(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index a711aed0..091d1c0d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -575,7 +575,7 @@ public class ImportOfficeData extends ContextBasedTest { if (isNotBlank(rec.getString("member_since"))) { final var membership = HsOfficeMembershipEntity.builder() .partner(partner) - .memberNumber(rec.getInteger("member_id")) + .memberNumberSuffix(rec.getInteger("member_id")) .validity(toPostgresDateRange( rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) -- 2.39.2 From acf48004b42323513ce44bab25812cc54334633f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 23 Jan 2024 10:32:07 +0100 Subject: [PATCH 63/72] fixes in data-model for data-import --- doc/hs-office-data-structure.md | 2 +- .../coopassets/HsOfficeCoopAssetsTransactionEntity.java | 6 ++++-- .../coopshares/HsOfficeCoopSharesTransactionEntity.java | 2 +- .../resources/db/changelog/240-hs-office-bankaccount.sql | 2 +- .../resources/db/changelog/320-hs-office-coopassets.sql | 3 ++- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/hs-office-data-structure.md b/doc/hs-office-data-structure.md index 56467c5d..960e572b 100644 --- a/doc/hs-office-data-structure.md +++ b/doc/hs-office-data-structure.md @@ -66,7 +66,7 @@ classDiagram +Numeric partnerNumber: 12345 +Role partnerRole } - partner-MeierGmbH o-- role-MeierGmbH + partner-MeierGmbH *-- role-MeierGmbH class person-MeierGmbH { +personType: LEGAL diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 41e5710f..67e46c80 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -1,3 +1,4 @@ + package net.hostsharing.hsadminng.hs.office.coopassets; import lombok.*; @@ -14,6 +15,7 @@ import java.text.DecimalFormat; import java.time.LocalDate; import java.util.UUID; +import static java.util.Optional.ofNullable; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @Entity @@ -27,7 +29,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid { private static Stringify stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class) - .withProp(e -> e.getMembership().getMemberNumberSuffix()) + .withProp(e -> ofNullable(e.getMembership()).map(HsOfficeMembershipEntity::getMemberNumberSuffix).orElse(null)) .withProp(HsOfficeCoopAssetsTransactionEntity::getValueDate) .withProp(HsOfficeCoopAssetsTransactionEntity::getTransactionType) .withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue) @@ -55,7 +57,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu private BigDecimal assetValue; @Column(name = "reference") - private String reference; + private String reference; // TODO: what is this for? @Column(name = "comment") private String comment; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index bf52837f..ec54255e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -51,7 +51,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu private int shareCount; @Column(name = "reference") - private String reference; + private String reference; // TODO: what is this for? @Column(name = "comment") private String comment; diff --git a/src/main/resources/db/changelog/240-hs-office-bankaccount.sql b/src/main/resources/db/changelog/240-hs-office-bankaccount.sql index bf3ed342..427b0199 100644 --- a/src/main/resources/db/changelog/240-hs-office-bankaccount.sql +++ b/src/main/resources/db/changelog/240-hs-office-bankaccount.sql @@ -6,7 +6,7 @@ create table hs_office_bankaccount ( uuid uuid unique references RbacObject (uuid) initially deferred, - holder varchar(27) not null, + holder varchar(64) not null, iban varchar(34) not null, bic varchar(11) not null ); diff --git a/src/main/resources/db/changelog/320-hs-office-coopassets.sql b/src/main/resources/db/changelog/320-hs-office-coopassets.sql index db76d95b..9a712f3a 100644 --- a/src/main/resources/db/changelog/320-hs-office-coopassets.sql +++ b/src/main/resources/db/changelog/320-hs-office-coopassets.sql @@ -10,7 +10,8 @@ CREATE TYPE HsOfficeCoopAssetsTransactionType AS ENUM ('ADJUSTMENT', 'TRANSFER', 'ADOPTION', 'CLEARING', - 'LOSS'); + 'LOSS', + 'LIMITATION'); CREATE CAST (character varying as HsOfficeCoopAssetsTransactionType) WITH INOUT AS IMPLICIT; -- 2.39.2 From e444f648b32575205be84ade4002a27a8f26f285 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 23 Jan 2024 10:32:38 +0100 Subject: [PATCH 64/72] split memberNumber into partnerNumber+memberNumberSuffix (WIP) --- .../HsOfficeMembershipController.java | 5 ++-- .../membership/HsOfficeMembershipEntity.java | 8 +++--- .../HsOfficeMembershipRepository.java | 27 ++++++++++++++---- .../db/changelog/220-hs-office-partner.sql | 2 +- .../db/changelog/300-hs-office-membership.sql | 3 +- .../308-hs-office-membership-test-data.sql | 28 +++++++++++-------- .../318-hs-office-coopshares-test-data.sql | 28 ++++++++++++------- .../328-hs-office-coopassets-test-data.sql | 28 ++++++++++++------- ...tsTransactionControllerAcceptanceTest.java | 8 +++--- ...sTransactionRepositoryIntegrationTest.java | 11 +++----- ...esTransactionControllerAcceptanceTest.java | 8 +++--- ...sTransactionRepositoryIntegrationTest.java | 11 ++++---- ...iceMembershipControllerAcceptanceTest.java | 22 +++++++-------- .../HsOfficeMembershipEntityUnitTest.java | 6 ++-- ...ceMembershipRepositoryIntegrationTest.java | 18 ++++++------ .../office/membership/TestHsMembership.java | 2 +- .../hs/office/migration/ImportOfficeData.java | 7 +++-- 17 files changed, 125 insertions(+), 97 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index b46b02a8..da1b7920 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -41,11 +41,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { final String currentUser, final String assumedRoles, UUID partnerUuid, - Integer memberNumberSuffix) { + Integer memberNumber) { context.define(currentUser, assumedRoles); - final var entities = - membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(partnerUuid, memberNumberSuffix); + final var entities = membershipRepo.findMembershipsByOptionalPartherNumber(partnerUuid); final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); 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 5991a9ac..89225f1f 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 @@ -31,7 +31,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { private static Stringify stringify = stringify(HsOfficeMembershipEntity.class) - .withProp(HsOfficeMembershipEntity::getMemberNumberSuffix) + .withProp(e -> e.getPartner().getPartnerNumber() + e.memberNumberSuffix) .withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getMainDebitor().toShortString()) .withProp(e -> e.getValidity().asString()) @@ -52,8 +52,8 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @JoinColumn(name = "maindebitoruuid") private HsOfficeDebitorEntity mainDebitor; - @Column(name = "membernumbersuffix") - private int memberNumberSuffix; + @Column(name = "membernumbersuffix", length = 2) + private String memberNumberSuffix; @Column(name = "validity", columnDefinition = "daterange") @Type(PostgreSQLRangeType.class) @@ -98,7 +98,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return String.valueOf(memberNumberSuffix); + return partner.getPartnerNumber() + String.valueOf(memberNumberSuffix); } @PrePersist diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index 1bee3f73..71f91881 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.membership; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; +import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -11,16 +12,30 @@ public interface HsOfficeMembershipRepository extends Repository findByUuid(UUID id); + HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity); + + @Query(""" SELECT membership FROM HsOfficeMembershipEntity membership - WHERE (:memberNumberSuffix is null OR membership.memberNumberSuffix = :memberNumberSuffix) - AND ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL - OR membership.partner.uuid = :partnerUuid ) + WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL + OR membership.partner.uuid = :partnerUuid ) + ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix + """) + List findMembershipsByOptionalPartherNumber(UUID partnerUuid); + @Query(""" + SELECT membership FROM HsOfficeMembershipEntity membership + WHERE (:partnerNumber = membership.partner.partnerNumber) + AND (membership.memberNumberSuffix = :suffix) ORDER BY membership.memberNumberSuffix """) - List findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(UUID partnerUuid, Integer memberNumberSuffix); - - HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity); + List findMembershipsByPartnerNumberAndSuffix( + @NotNull Integer partnerNumber, + @NotNull String suffix); + default List findMembershipsByMemberNumber(Integer memberNumber) { + final var partnerNumber = memberNumber / 100; + final var suffix = memberNumber % 100; + return findMembershipsByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix)); + } long count(); diff --git a/src/main/resources/db/changelog/220-hs-office-partner.sql b/src/main/resources/db/changelog/220-hs-office-partner.sql index 67126766..c4491b0a 100644 --- a/src/main/resources/db/changelog/220-hs-office-partner.sql +++ b/src/main/resources/db/changelog/220-hs-office-partner.sql @@ -32,7 +32,7 @@ call create_journal('hs_office_partner_details'); create table hs_office_partner ( uuid uuid unique references RbacObject (uuid) initially deferred, - partnerNumber varchar(5), + partnerNumber numeric(5), personUuid uuid not null references hs_office_person(uuid), contactUuid uuid not null references hs_office_contact(uuid), detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index 750fd680..01e644cd 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -13,7 +13,8 @@ create table if not exists hs_office_membership uuid uuid unique references RbacObject (uuid) initially deferred, partnerUuid uuid not null references hs_office_partner(uuid), mainDebitorUuid uuid not null references hs_office_debitor(uuid), - memberNumberSuffix numeric(5) not null unique, + memberNumberSuffix char(2) not null check ( + memberNumberSuffix::text ~ '^[0-9][0-9]$'), validity daterange not null, reasonForTermination HsOfficeReasonForTermination not null default 'NONE', membershipFeeBillable boolean not null default true diff --git a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql index 6d182650..637c87ca 100644 --- a/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql +++ b/src/main/resources/db/changelog/308-hs-office-membership-test-data.sql @@ -8,16 +8,18 @@ /* Creates a single membership test record. */ -create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric ) +create or replace procedure createHsOfficeMembershipTestData( + forPartnerTradeName varchar, + forMainDebitorNumberSuffix numeric, + newMemberNumberSuffix char(2) ) language plpgsql as $$ declare - currentTask varchar; - idName varchar; - relatedPartner hs_office_partner; - relatedDebitor hs_office_debitor; - newMemberNumberSuffix numeric; + currentTask varchar; + idName varchar; + relatedPartner hs_office_partner; + relatedDebitor hs_office_debitor; begin - idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumber); + idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumberSuffix); currentTask := 'creating Membership test-data ' || idName; call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin'); execute format('set local hsadminng.currentTask to %L', currentTask); @@ -25,8 +27,10 @@ begin select partner.* from hs_office_partner partner join hs_office_person person on person.uuid = partner.personUuid where person.tradeName = forPartnerTradeName into relatedPartner; - select d.* from hs_office_debitor d where d.debitorNumberSuffix = forMainDebitorNumber into relatedDebitor; - select coalesce(max(memberNumberSuffix)+1, 10001) from hs_office_membership into newMemberNumberSuffix; + select d.* from hs_office_debitor d + where d.partneruuid = relatedPartner.uuid + and d.debitorNumberSuffix = forMainDebitorNumberSuffix + into relatedDebitor; raise notice 'creating test Membership: %', idName; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; @@ -44,9 +48,9 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeMembershipTestData('First GmbH', 11); - call createHsOfficeMembershipTestData('Second e.K.', 12); - call createHsOfficeMembershipTestData('Third OHG', 13); + call createHsOfficeMembershipTestData('First GmbH', 11, '01'); + call createHsOfficeMembershipTestData('Second e.K.', 12, '02'); + call createHsOfficeMembershipTestData('Third OHG', 13, '03'); end; $$; --// diff --git a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql index 987f0aba..2bef88af 100644 --- a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql +++ b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql @@ -8,25 +8,33 @@ /* Creates a single coopSharesTransaction test record. */ -create or replace procedure createHsOfficeCoopSharesTransactionTestData(givenMembershipNumber numeric) +create or replace procedure createHsOfficeCoopSharesTransactionTestData( + givenPartnerNumber numeric, + givenMemberNumberSuffix varchar -- TODO char(2)? +) language plpgsql as $$ declare currentTask varchar; membership hs_office_membership; begin - currentTask = 'creating coopSharesTransaction test-data ' || givenMembershipNumber; + currentTask = 'creating coopSharesTransaction test-data ' || givenPartnerNumber::text || givenMemberNumberSuffix; execute format('set local hsadminng.currentTask to %L', currentTask); call defineContext(currentTask); - select m.uuid from hs_office_membership m where m.memberNumberSuffix = givenMembershipNumber into membership; + select m.uuid + from hs_office_membership m + join hs_office_partner p on p.uuid = m.partneruuid + where p.partnerNumber = givenPartnerNumber + and m.memberNumberSuffix = givenMemberNumberSuffix + into membership; - raise notice 'creating test coopSharesTransaction: %', givenMembershipNumber; + raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix; insert into hs_office_coopsharestransaction(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment) values - (uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenMembershipNumber||'-1', 'initial subscription'), - (uuid_generate_v4(), membership.uuid, 'CANCELLATION', '2021-09-01', -2, 'ref '||givenMembershipNumber||'-2', 'cancelling some'), - (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 2, 'ref '||givenMembershipNumber||'-3', 'some adjustment'); + (uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-1', 'initial subscription'), + (uuid_generate_v4(), membership.uuid, 'CANCELLATION', '2021-09-01', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-2', 'cancelling some'), + (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some adjustment'); end; $$; --// @@ -37,8 +45,8 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeCoopSharesTransactionTestData(10001); - call createHsOfficeCoopSharesTransactionTestData(10002); - call createHsOfficeCoopSharesTransactionTestData(10003); + call createHsOfficeCoopSharesTransactionTestData(10001, '01'); + call createHsOfficeCoopSharesTransactionTestData(10002, '02'); + call createHsOfficeCoopSharesTransactionTestData(10003, '03'); end; $$; diff --git a/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql b/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql index 4589ceb6..d54e77ca 100644 --- a/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql +++ b/src/main/resources/db/changelog/328-hs-office-coopassets-test-data.sql @@ -8,25 +8,33 @@ /* Creates a single coopAssetsTransaction test record. */ -create or replace procedure createHsOfficeCoopAssetsTransactionTestData(givenMembershipNumber numeric) +create or replace procedure createHsOfficeCoopAssetsTransactionTestData( + givenPartnerNumber numeric, + givenMemberNumberSuffix char(2) + ) language plpgsql as $$ declare currentTask varchar; membership hs_office_membership; begin - currentTask = 'creating coopAssetsTransaction test-data ' || givenMembershipNumber; + currentTask = 'creating coopAssetsTransaction test-data ' || givenPartnerNumber || givenMemberNumberSuffix; execute format('set local hsadminng.currentTask to %L', currentTask); call defineContext(currentTask); - select m.uuid from hs_office_membership m where m.memberNumberSuffix = givenMembershipNumber into membership; + select m.uuid + from hs_office_membership m + join hs_office_partner p on p.uuid = m.partneruuid + where p.partnerNumber = givenPartnerNumber + and m.memberNumberSuffix = givenMemberNumberSuffix + into membership; - raise notice 'creating test coopAssetsTransaction: %', givenMembershipNumber; + raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix; insert into hs_office_coopassetstransaction(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment) values - (uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenMembershipNumber||'-1', 'initial deposit'), - (uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenMembershipNumber||'-2', 'partial disbursal'), - (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 128.00, 'ref '||givenMembershipNumber||'-3', 'some adjustment'); + (uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit'), + (uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal'), + (uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment'); end; $$; --// @@ -37,8 +45,8 @@ end; $$; do language plpgsql $$ begin - call createHsOfficeCoopAssetsTransactionTestData(10001); - call createHsOfficeCoopAssetsTransactionTestData(10002); - call createHsOfficeCoopAssetsTransactionTestData(10003); + call createHsOfficeCoopAssetsTransactionTestData(10001, '01'); + call createHsOfficeCoopAssetsTransactionTestData(10002, '02'); + call createHsOfficeCoopAssetsTransactionTestData(10003, '03'); end; $$; diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index edf0eed3..c227456d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -75,7 +75,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) .get(0); RestAssured // @formatter:off @@ -118,7 +118,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) .get(0); RestAssured // @formatter:off @@ -153,7 +153,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopAssetsTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0); final var location = RestAssured // @formatter:off @@ -199,7 +199,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0); final var location = RestAssured // @formatter:off diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index bf9cc649..0db985ff 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -62,7 +62,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopAssetsTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0); // when @@ -96,9 +96,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - null, - 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) @@ -161,8 +159,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) - .get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(null).get(1000202); // when final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( @@ -182,7 +179,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) .get(0); // when diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index cd4aaffa..1212046e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -75,7 +75,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -109,7 +109,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -134,7 +134,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopSharesTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" @@ -165,7 +165,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index 8b0c607e..85c3ebc5 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -61,7 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopSharesTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10001) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0); // when @@ -95,9 +95,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - null, - 10001).get(0); + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) + .get(0); final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION) @@ -160,7 +159,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) .get(0); // when @@ -181,7 +180,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002) + final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) .get(0); // when diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 1f83ae07..a5058401 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -61,7 +61,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @PersistenceContext EntityManager em; - private static int tempMemberNumberSuffix = 20010; + private static int tempMemberNumberSuffix = 10; @Nested @Accepts({ "Membership:F(Find)" }) @@ -166,9 +166,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Test void globalAdmin_canGetArbitraryMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - null, - 10001) + final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0) .getUuid(); @@ -197,9 +195,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void normalUser_canNotGetUnrelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - null, - 10001) + final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) .get(0) .getUuid(); @@ -217,9 +213,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void debitorAgentUser_canGetRelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - null, - 10003) + final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000303) .get(0) .getUuid(); @@ -444,7 +438,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .uuid(UUID.randomUUID()) .partner(givenPartner) .mainDebitor(givenDebitor) - .memberNumberSuffix(++tempMemberNumberSuffix) + .memberNumberSuffix(toPaddedSuffix(++tempMemberNumberSuffix)) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .reasonForTermination(NONE) .membershipFeeBillable(true) @@ -454,12 +448,16 @@ class HsOfficeMembershipControllerAcceptanceTest { }).assertSuccessful().returnedValue(); } + private String toPaddedSuffix(final int numericSuffix) { + return String.format("%02d", numericSuffix); + } + @BeforeEach @AfterEach void cleanup() { jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net", null); - final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= 20000"); + final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '20'"); query.executeUpdate(); }); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index 5ba69bbc..0b8d1342 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -17,7 +17,7 @@ class HsOfficeMembershipEntityUnitTest { public static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-01-01"); final HsOfficeMembershipEntity givenMembership = HsOfficeMembershipEntity.builder() - .memberNumberSuffix(10001) + .memberNumberSuffix("01") .partner(TEST_PARTNER) .mainDebitor(TEST_DEBITOR) .validity(Range.closedInfinite(GIVEN_VALID_FROM)) @@ -27,14 +27,14 @@ class HsOfficeMembershipEntityUnitTest { void toStringContainsAllProps() { final var result = givenMembership.toString(); - assertThat(result).isEqualTo("Membership(10001, LEGAL Test Ltd., 1000100, [2020-01-01,))"); + assertThat(result).isEqualTo("Membership(1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))"); } @Test void toShortStringContainsMemberNumberSuffixOnly() { final var result = givenMembership.toShortString(); - assertThat(result).isEqualTo("10001"); + assertThat(result).isEqualTo("1000101"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index c9a8eb0d..376d1ecd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -77,7 +77,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // when final var result = attempt(em, () -> { final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumberSuffix(20001) + .memberNumberSuffix("01") .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) @@ -108,7 +108,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumberSuffix(20002) + .memberNumberSuffix("02") .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) @@ -179,7 +179,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, null); + final var result = membershipRepo.findMembershipsByOptionalPartherNumber(null); // then exactlyTheseMembershipsAreReturned( @@ -196,21 +196,19 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix( - givenPartner.getUuid(), - null); + final var result = membershipRepo.findMembershipsByOptionalPartherNumber(givenPartner.getUuid()); // then exactlyTheseMembershipsAreReturned(result, "Membership(10001, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); } @Test - public void globalAdmin_withoutAssumedRole_canFindAllMembershipByMemberNumberSuffix() { + public void globalAdmin_withoutAssumedRole_canFindAllMemberships() { // given context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumberSuffix(null, 10002); + final var result = membershipRepo.findMembershipsByOptionalPartherNumber(null); // then exactlyTheseMembershipsAreReturned(result, "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); @@ -401,7 +399,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { }); jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net", null); - em.createQuery("DELETE FROM HsOfficeMembershipEntity WHERE memberNumberSuffix >= 20000"); + em.createQuery("DELETE FROM HsOfficeMembershipEntity WHERE memberNumberSuffix >= '20'"); }); } @@ -411,7 +409,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partnerTradeName).get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike(debitorName).get(0); final var newMembership = HsOfficeMembershipEntity.builder() - .memberNumberSuffix(20002) + .memberNumberSuffix("02") .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java index 3fc8c471..ff50eb58 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/TestHsMembership.java @@ -11,7 +11,7 @@ public class TestHsMembership { public static final HsOfficeMembershipEntity TEST_MEMBERSHIP = HsOfficeMembershipEntity.builder() .partner(TEST_PARTNER) - .memberNumberSuffix(300001) + .memberNumberSuffix("01") .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) .build(); } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 091d1c0d..b4f97f24 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -573,9 +573,10 @@ public class ImportOfficeData extends ContextBasedTest { partners.put(rec.getInteger("bp_id"), partner); if (isNotBlank(rec.getString("member_since"))) { + assertThat(rec.getInteger("member_id")).isEqualTo(partner.getPartnerNumber()); final var membership = HsOfficeMembershipEntity.builder() .partner(partner) - .memberNumberSuffix(rec.getInteger("member_id")) + .memberNumberSuffix("00") .validity(toPostgresDateRange( rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) @@ -612,7 +613,7 @@ public class ImportOfficeData extends ContextBasedTest { : HsOfficeCoopSharesTransactionType.ADJUSTMENT ) .shareCount(rec.getInteger("quantity")) - .reference(rec.getString("comment")) + .comment( rec.getString("comment")) .build(); coopShares.put(rec.getInteger("member_share_id"), shareTransaction); @@ -655,7 +656,7 @@ public class ImportOfficeData extends ContextBasedTest { .valueDate(rec.getLocalDate("date")) .transactionType(assetTypeMapping.get(rec.getString("action"))) .assetValue(rec.getBigDecimal("amount")) - .reference(rec.getString("comment")) + .comment(rec.getString("comment")) .build(); coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction); -- 2.39.2 From bcf8ad21484786c3e4f68360632048114463093c Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Tue, 23 Jan 2024 17:46:52 +0100 Subject: [PATCH 65/72] combined memberNumber --- .../HsOfficeCoopAssetsTransactionEntity.java | 2 +- .../HsOfficeCoopSharesTransactionEntity.java | 2 +- .../HsOfficeMembershipController.java | 2 +- .../membership/HsOfficeMembershipEntity.java | 29 ++++++-- .../HsOfficeMembershipRepository.java | 2 +- .../303-hs-office-membership-rbac.sql | 5 +- ...tsTransactionControllerAcceptanceTest.java | 8 +-- ...ceCoopAssetsTransactionEntityUnitTest.java | 4 +- ...sTransactionRepositoryIntegrationTest.java | 38 +++++------ ...esTransactionControllerAcceptanceTest.java | 8 +-- ...ceCoopSharesTransactionEntityUnitTest.java | 4 +- ...sTransactionRepositoryIntegrationTest.java | 4 +- .../HsOfficeMembershipEntityUnitTest.java | 4 +- ...ceMembershipRepositoryIntegrationTest.java | 66 ++++++++++--------- .../hs/office/migration/ImportOfficeData.java | 6 +- 15 files changed, 104 insertions(+), 80 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 0f1b3600..02c72e17 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -58,7 +58,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu private BigDecimal assetValue; @Column(name = "reference") - private String reference; // TODO: what is this for? + private String reference; @Column(name = "comment") private String comment; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 6c47bbb8..749b616c 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -53,7 +53,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu private int shareCount; @Column(name = "reference") - private String reference; // TODO: what is this for? + private String reference; @Column(name = "comment") private String comment; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index da1b7920..a375ffa4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -44,7 +44,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { Integer memberNumber) { context.define(currentUser, assumedRoles); - final var entities = membershipRepo.findMembershipsByOptionalPartherNumber(partnerUuid); + final var entities = membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); 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 52ca596f..703adc57 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 @@ -15,6 +15,7 @@ import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.time.LocalDate; +import java.util.Optional; import java.util.UUID; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; @@ -30,8 +31,10 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @DisplayName("Membership") public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { + public static final String MEMBER_NUMBER_TAG = "M-"; + private static Stringify stringify = stringify(HsOfficeMembershipEntity.class) - .withProp(HsOfficeMembershipEntity::getMemberNumber) + .withProp(HsOfficeMembershipEntity::getMemberNumberString) .withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getMainDebitor().toShortString()) .withProp(e -> e.getValidity().asString()) @@ -82,15 +85,33 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { return upperInclusiveFromPostgresDateRange(getValidity()); } - public Range getValidity() { if (validity == null) { validity = Range.infinite(LocalDate.class); } - ; return validity; } + public String getMemberNumberString() { + return MEMBER_NUMBER_TAG + getMemberNumber(); + } + + public Integer getMemberNumber() { + // TODO: refactor + String combinedMemberNumber; + if (partner.getPartnerNumber() == null ) { + if (memberNumberSuffix == null) { + combinedMemberNumber = null; + } else {combinedMemberNumber = MEMBER_NUMBER_TAG + memberNumberSuffix;} + } else if (memberNumberSuffix == null) { + combinedMemberNumber = partner.getPartnerNumber() + "??"; + } else { + combinedMemberNumber = partner.getPartnerNumber() + memberNumberSuffix; + } + + return Optional.ofNullable(combinedMemberNumber).map(Integer::parseInt).orElse(null); + } + @Override public String toString() { return stringify.apply(this); @@ -98,7 +119,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return partner.getPartnerNumber() + String.valueOf(memberNumberSuffix); + return "M-" + partner.getPartnerNumber() + String.valueOf(memberNumberSuffix); } @PrePersist diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index 71f91881..6de92ee8 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -21,7 +21,7 @@ public interface HsOfficeMembershipRepository extends Repository findMembershipsByOptionalPartherNumber(UUID partnerUuid); + List findMembershipsByOptionalPartnerUuid(UUID partnerUuid); @Query(""" SELECT membership FROM HsOfficeMembershipEntity membership WHERE (:partnerNumber = membership.partner.partnerNumber) diff --git a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql index de7b9c30..8197cf09 100644 --- a/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql +++ b/src/main/resources/db/changelog/303-hs-office-membership-rbac.sql @@ -92,8 +92,9 @@ execute procedure hsOfficeMembershipRbacRolesTrigger(); --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$ - target.memberNumberSuffix || - ':' || + '#' || + (select partnerNumber from hs_office_partner p where p.uuid = target.partnerUuid) || + memberNumberSuffix || ':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid) $idName$); --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index c227456d..a7052a2e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -93,21 +93,21 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { "transactionType": "DEPOSIT", "assetValue": 320.00, "valueDate": "2010-03-15", - "reference": "ref 10002-1", + "reference": "ref 1000202-1", "comment": "initial deposit" }, { "transactionType": "DISBURSAL", "assetValue": -128.00, "valueDate": "2021-09-01", - "reference": "ref 10002-2", + "reference": "ref 1000202-2", "comment": "partial disbursal" }, { "transactionType": "ADJUSTMENT", "assetValue": 128.00, "valueDate": "2022-10-20", - "reference": "ref 10002-3", + "reference": "ref 1000202-3", "comment": "some adjustment" } ] @@ -137,7 +137,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { "transactionType": "DISBURSAL", "assetValue": -128.00, "valueDate": "2021-09-01", - "reference": "ref 10002-2", + "reference": "ref 1000202-2", "comment": "partial disbursal" } ] diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java index 6c21cde8..d93aa90f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntityUnitTest.java @@ -23,14 +23,14 @@ class HsOfficeCoopAssetsTransactionEntityUnitTest { void toStringContainsAlmostAllPropertiesAccount() { final var result = givenCoopAssetTransaction.toString(); - assertThat(result).isEqualTo("CoopAssetsTransaction(300001, 2020-01-01, DEPOSIT, 128.00, some-ref)"); + assertThat(result).isEqualTo("CoopAssetsTransaction(1000101, 2020-01-01, DEPOSIT, 128.00, some-ref)"); } @Test void toShortStringContainsOnlyMemberNumberSuffixAndSharesCountOnly() { final var result = givenCoopAssetTransaction.toShortString(); - assertThat(result).isEqualTo("300001+128.00"); + assertThat(result).isEqualTo("1000101+128.00"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index ce50f4a7..dfba6b7a 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -115,7 +115,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm view on coopassetstransaction#temprefB to role membership#10001:....tenant by system and assume }", + "{ grant perm view on coopassetstransaction#temprefB to role membership#1000101:....tenant by system and assume }", null)); } @@ -142,17 +142,17 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopAssetsTransactionsAreReturned( result, - "CoopAssetsTransaction(10001, 2010-03-15, DEPOSIT, 320.00, ref 10001-1, initial deposit)", - "CoopAssetsTransaction(10001, 2021-09-01, DISBURSAL, -128.00, ref 10001-2, partial disbursal)", - "CoopAssetsTransaction(10001, 2022-10-20, ADJUSTMENT, 128.00, ref 10001-3, some adjustment)", + "CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)", + "CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)", + "CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)", - "CoopAssetsTransaction(10002, 2010-03-15, DEPOSIT, 320.00, ref 10002-1, initial deposit)", - "CoopAssetsTransaction(10002, 2021-09-01, DISBURSAL, -128.00, ref 10002-2, partial disbursal)", - "CoopAssetsTransaction(10002, 2022-10-20, ADJUSTMENT, 128.00, ref 10002-3, some adjustment)", + "CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)", + "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)", + "CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)", - "CoopAssetsTransaction(10003, 2010-03-15, DEPOSIT, 320.00, ref 10003-1, initial deposit)", - "CoopAssetsTransaction(10003, 2021-09-01, DISBURSAL, -128.00, ref 10003-2, partial disbursal)", - "CoopAssetsTransaction(10003, 2022-10-20, ADJUSTMENT, 128.00, ref 10003-3, some adjustment)"); + "CoopAssetsTransaction(1000303, 2010-03-15, DEPOSIT, 320.00, ref 1000303-1, initial deposit)", + "CoopAssetsTransaction(1000303, 2021-09-01, DISBURSAL, -128.00, ref 1000303-2, partial disbursal)", + "CoopAssetsTransaction(1000303, 2022-10-20, ADJUSTMENT, 128.00, ref 1000303-3, some adjustment)"); } @Test @@ -170,9 +170,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopAssetsTransactionsAreReturned( result, - "CoopAssetsTransaction(10002, 2010-03-15, DEPOSIT, 320.00, ref 10002-1, initial deposit)", - "CoopAssetsTransaction(10002, 2021-09-01, DISBURSAL, -128.00, ref 10002-2, partial disbursal)", - "CoopAssetsTransaction(10002, 2022-10-20, ADJUSTMENT, 128.00, ref 10002-3, some adjustment)"); + "CoopAssetsTransaction(1000202, 2010-03-15, DEPOSIT, 320.00, ref 1000202-1, initial deposit)", + "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)", + "CoopAssetsTransaction(1000202, 2022-10-20, ADJUSTMENT, 128.00, ref 1000202-3, some adjustment)"); } @Test @@ -191,7 +191,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopAssetsTransactionsAreReturned( result, - "CoopAssetsTransaction(10002, 2021-09-01, DISBURSAL, -128.00, ref 10002-2, partial disbursal)"); + "CoopAssetsTransaction(1000202, 2021-09-01, DISBURSAL, -128.00, ref 1000202-2, partial disbursal)"); } @Test @@ -208,9 +208,9 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // then: exactlyTheseCoopAssetsTransactionsAreReturned( result, - "CoopAssetsTransaction(10001, 2010-03-15, DEPOSIT, 320.00, ref 10001-1, initial deposit)", - "CoopAssetsTransaction(10001, 2021-09-01, DISBURSAL, -128.00, ref 10001-2, partial disbursal)", - "CoopAssetsTransaction(10001, 2022-10-20, ADJUSTMENT, 128.00, ref 10001-3, some adjustment)"); + "CoopAssetsTransaction(1000101, 2010-03-15, DEPOSIT, 320.00, ref 1000101-1, initial deposit)", + "CoopAssetsTransaction(1000101, 2021-09-01, DISBURSAL, -128.00, ref 1000101-2, partial disbursal)", + "CoopAssetsTransaction(1000101, 2022-10-20, ADJUSTMENT, 128.00, ref 1000101-3, some adjustment)"); } } @@ -229,8 +229,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // then assertThat(customerLogEntries).map(Arrays::toString).contains( - "[creating coopAssetsTransaction test-data 10001, hs_office_coopassetstransaction, INSERT]", - "[creating coopAssetsTransaction test-data 10002, hs_office_coopassetstransaction, INSERT]"); + "[creating coopAssetsTransaction test-data 1000101, hs_office_coopassetstransaction, INSERT]", + "[creating coopAssetsTransaction test-data 1000202, hs_office_coopassetstransaction, INSERT]"); } @BeforeEach diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index 1212046e..ebb7ca59 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -84,21 +84,21 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { "transactionType": "SUBSCRIPTION", "shareCount": 4, "valueDate": "2010-03-15", - "reference": "ref 10002-1", + "reference": "ref 1000202-1", "comment": "initial subscription" }, { "transactionType": "CANCELLATION", "shareCount": -2, "valueDate": "2021-09-01", - "reference": "ref 10002-2", + "reference": "ref 1000202-2", "comment": "cancelling some" }, { "transactionType": "ADJUSTMENT", "shareCount": 2, "valueDate": "2022-10-20", - "reference": "ref 10002-3", + "reference": "ref 1000202-3", "comment": "some adjustment" } ] @@ -118,7 +118,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { "transactionType": "CANCELLATION", "shareCount": -2, "valueDate": "2021-09-01", - "reference": "ref 10002-2", + "reference": "ref 1000202-2", "comment": "cancelling some" } ] diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java index 8dcfd133..862c593e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java @@ -22,14 +22,14 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { void toStringContainsAlmostAllPropertiesAccount() { final var result = givenCoopSharesTransaction.toString(); - assertThat(result).isEqualTo("CoopShareTransaction(300001, 2020-01-01, SUBSCRIPTION, 4, some-ref)"); + assertThat(result).isEqualTo("CoopShareTransaction(1000101, 2020-01-01, SUBSCRIPTION, 4, some-ref)"); } @Test void toShortStringContainsOnlyMemberNumberAndShareCountOnly() { final var result = givenCoopSharesTransaction.toShortString(); - assertThat(result).isEqualTo("300001+4"); + assertThat(result).isEqualTo("1000101+4"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index d6ec4269..9ef64522 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -230,8 +230,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // then assertThat(customerLogEntries).map(Arrays::toString).contains( - "[creating coopSharesTransaction test-data 10001, hs_office_coopsharestransaction, INSERT]", - "[creating coopSharesTransaction test-data 10002, hs_office_coopsharestransaction, INSERT]"); + "[creating coopSharesTransaction test-data 1000101, hs_office_coopsharestransaction, INSERT]", + "[creating coopSharesTransaction test-data 1000202, hs_office_coopsharestransaction, INSERT]"); } @BeforeEach diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index 0b8d1342..9563d6ad 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -27,14 +27,14 @@ class HsOfficeMembershipEntityUnitTest { void toStringContainsAllProps() { final var result = givenMembership.toString(); - assertThat(result).isEqualTo("Membership(1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))"); + assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))"); } @Test void toShortStringContainsMemberNumberSuffixOnly() { final var result = givenMembership.toShortString(); - assertThat(result).isEqualTo("1000101"); + assertThat(result).isEqualTo("M-1000101"); } @Test diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 376d1ecd..e6ec5bb2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -108,7 +108,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("First").get(0); final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumberSuffix("02") + .memberNumberSuffix("07") .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) @@ -121,11 +121,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var all = rawRoleRepo.findAll(); assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_membership#20002:FirstGmbH-firstcontact.admin", - "hs_office_membership#20002:FirstGmbH-firstcontact.agent", - "hs_office_membership#20002:FirstGmbH-firstcontact.guest", - "hs_office_membership#20002:FirstGmbH-firstcontact.owner", - "hs_office_membership#20002:FirstGmbH-firstcontact.tenant")); + "hs_office_membership#1000107:FirstGmbH-firstcontact.admin", + "hs_office_membership#1000107:FirstGmbH-firstcontact.agent", + "hs_office_membership#1000107:FirstGmbH-firstcontact.guest", + "hs_office_membership#1000107:FirstGmbH-firstcontact.owner", + "hs_office_membership#1000107:FirstGmbH-firstcontact.tenant")); assertThat(grantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("GmbH-firstcontact", "")) .map(s -> s.replace("hs_office_", "")) @@ -133,33 +133,33 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { initialGrantNames, // owner - "{ grant perm * on membership#20002:First to role membership#20002:First.owner by system and assume }", - "{ grant role membership#20002:First.owner to role global#global.admin by system and assume }", + "{ grant perm * on membership#1000107:First to role membership#1000107:First.owner by system and assume }", + "{ grant role membership#1000107:First.owner to role global#global.admin by system and assume }", // admin - "{ grant perm edit on membership#20002:First to role membership#20002:First.admin by system and assume }", - "{ grant role membership#20002:First.admin to role membership#20002:First.owner by system and assume }", + "{ grant perm edit on membership#1000107:First to role membership#1000107:First.admin by system and assume }", + "{ grant role membership#1000107:First.admin to role membership#1000107:First.owner by system and assume }", // agent - "{ grant role membership#20002:First.agent to role membership#20002:First.admin by system and assume }", - "{ grant role partner#10001:First.tenant to role membership#20002:First.agent by system and assume }", - "{ grant role membership#20002:First.agent to role debitor#1000111:First.admin by system and assume }", - "{ grant role membership#20002:First.agent to role partner#10001:First.admin by system and assume }", - "{ grant role debitor#1000111:First.tenant to role membership#20002:First.agent by system and assume }", + "{ grant role membership#1000107:First.agent to role membership#1000107:First.admin by system and assume }", + "{ grant role partner#10001:First.tenant to role membership#1000107:First.agent by system and assume }", + "{ grant role membership#1000107:First.agent to role debitor#1000111:First.admin by system and assume }", + "{ grant role membership#1000107:First.agent to role partner#10001:First.admin by system and assume }", + "{ grant role debitor#1000111:First.tenant to role membership#1000107:First.agent by system and assume }", // tenant - "{ grant role membership#20002:First.tenant to role membership#20002:First.agent by system and assume }", - "{ grant role partner#10001:First.guest to role membership#20002:First.tenant by system and assume }", - "{ grant role debitor#1000111:First.guest to role membership#20002:First.tenant by system and assume }", - "{ grant role membership#20002:First.tenant to role debitor#1000111:First.agent by system and assume }", + "{ grant role membership#1000107:First.tenant to role membership#1000107:First.agent by system and assume }", + "{ grant role partner#10001:First.guest to role membership#1000107:First.tenant by system and assume }", + "{ grant role debitor#1000111:First.guest to role membership#1000107:First.tenant by system and assume }", + "{ grant role membership#1000107:First.tenant to role debitor#1000111:First.agent by system and assume }", - "{ grant role membership#20002:First.tenant to role partner#10001:First.agent by system and assume }", + "{ grant role membership#1000107:First.tenant to role partner#10001:First.agent by system and assume }", // guest - "{ grant perm view on membership#20002:First to role membership#20002:First.guest by system and assume }", - "{ grant role membership#20002:First.guest to role membership#20002:First.tenant by system and assume }", - "{ grant role membership#20002:First.guest to role partner#10001:First.tenant by system and assume }", - "{ grant role membership#20002:First.guest to role debitor#1000111:First.tenant by system and assume }", + "{ grant perm view on membership#1000107:First to role membership#1000107:First.guest by system and assume }", + "{ grant role membership#1000107:First.guest to role membership#1000107:First.tenant by system and assume }", + "{ grant role membership#1000107:First.guest to role partner#10001:First.tenant by system and assume }", + "{ grant role membership#1000107:First.guest to role debitor#1000111:First.tenant by system and assume }", null)); } @@ -179,14 +179,14 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartherNumber(null); + final var result = membershipRepo.findMembershipsByOptionalPartnerUuid(null); // then exactlyTheseMembershipsAreReturned( result, - "Membership(10001, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)", - "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)", - "Membership(10003, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)"); + "Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)", + "Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)", + "Membership(M-1000303, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)"); } @Test @@ -196,10 +196,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); // when - final var result = membershipRepo.findMembershipsByOptionalPartherNumber(givenPartner.getUuid()); + final var result = membershipRepo.findMembershipsByOptionalPartnerUuid(givenPartner.getUuid()); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10001, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, + "Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); } @Test @@ -208,10 +209,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByOptionalPartherNumber(null); + final var result = membershipRepo.findMembershipsByMemberNumber(1000202); // then - exactlyTheseMembershipsAreReturned(result, "Membership(10002, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); + exactlyTheseMembershipsAreReturned(result, + "Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 28e20792..473807eb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -182,9 +182,9 @@ public class ImportOfficeData extends ContextBasedTest { """); assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { - 17=Membership(10017, null null, null, 1001700, [2000-12-06,), NONE), - 20=Membership(10020, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), - 22=Membership(11022, null null, null, 1102200, [2021-04-01,), NONE) + 17=Membership(1001700, null null, null, 1001700, [2000-12-06,), NONE), + 20=Membership(1002000, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), + 22=Membership(1102200, null null, null, 1102200, [2021-04-01,), NONE) } """); } -- 2.39.2 From 51f406c84ebdf667726c8a977877847b5fabdbae Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 12:45:52 +0100 Subject: [PATCH 66/72] combined memberNumber (partnerNumber+memberNumberSuffix) --- .aliases | 1 + .../HsOfficeCoopAssetsTransactionEntity.java | 1 - .../office/debitor/HsOfficeDebitorEntity.java | 19 +- .../HsOfficeMembershipController.java | 4 +- .../membership/HsOfficeMembershipEntity.java | 23 +- .../HsOfficeMembershipRepository.java | 6 +- .../partner/HsOfficePartnerRepository.java | 1 + .../hs-office-membership-schemas.yaml | 15 +- .../hs-office/hs-office-memberships.yaml | 9 +- ...tsTransactionControllerAcceptanceTest.java | 12 +- ...sTransactionRepositoryIntegrationTest.java | 10 +- ...esTransactionControllerAcceptanceTest.java | 8 +- ...sTransactionRepositoryIntegrationTest.java | 46 ++- .../HsOfficeDebitorEntityUnitTest.java | 60 +++- ...fficeDebitorRepositoryIntegrationTest.java | 14 +- ...iceMembershipControllerAcceptanceTest.java | 108 +++++-- .../HsOfficeMembershipControllerRestTest.java | 273 +++++++++++------- .../HsOfficeMembershipEntityUnitTest.java | 39 ++- ...ceMembershipRepositoryIntegrationTest.java | 16 +- ...fficePartnerRepositoryIntegrationTest.java | 19 ++ .../HsOfficeRelationshipEntityUnitTest.java | 32 ++ ...ceSepaMandateControllerAcceptanceTest.java | 4 +- 22 files changed, 482 insertions(+), 238 deletions(-) create mode 100644 src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java diff --git a/.aliases b/.aliases index c0b4e22d..f6673bcd 100644 --- a/.aliases +++ b/.aliases @@ -80,3 +80,4 @@ alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l' alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' alias gw-test='. .aliases; ./gradlew test' +alias gw-check='. .aliases; gw check -x pitest -x :dependencyCheckAnalyze' diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index 02c72e17..f0b91c07 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -11,7 +11,6 @@ import org.hibernate.annotations.GenericGenerator; import jakarta.persistence.*; import java.math.BigDecimal; -import java.text.DecimalFormat; import java.time.LocalDate; import java.util.UUID; 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 74787b0d..279f1d63 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 @@ -26,12 +26,14 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; @DisplayName("Debitor") public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { + public static final String DEBITOR_NUMBER_TAG = "D-"; + // TODO: I would rather like to generate something matching this example: // debitor(1234500: Test AG, tes) // maybe remove withSepararator (always use ', ') and add withBusinessIdProp (with ': ' afterwards)? private static Stringify stringify = stringify(HsOfficeDebitorEntity.class, "debitor") - .withProp(HsOfficeDebitorEntity::getDebitorNumber) + .withProp(e -> DEBITOR_NUMBER_TAG + e.getDebitorNumber()) .withProp(HsOfficeDebitorEntity::getPartner) .withProp(HsOfficeDebitorEntity::getDefaultPrefix) .withSeparator(": ") @@ -75,16 +77,9 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { @Column(name = "defaultprefix", columnDefinition = "char(3) not null") private String defaultPrefix; - public String getDebitorNumberString() { - // TODO: refactor - if (partner.getPartnerNumber() == null ) { - if (debitorNumberSuffix == null) { - return null; - } - return String.format("%02d", debitorNumberSuffix); - } - if (debitorNumberSuffix == null) { - return partner.getPartnerNumber() + "??"; + private String getDebitorNumberString() { + if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null ) { + return null; } return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix); } @@ -100,6 +95,6 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return getDebitorNumberString(); + return DEBITOR_NUMBER_TAG + getDebitorNumberString(); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java index a375ffa4..e18fc183 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipController.java @@ -44,7 +44,9 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi { Integer memberNumber) { context.define(currentUser, assumedRoles); - final var entities = membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); + final var entities = ( memberNumber != null) + ? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber)) + : membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid); final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class, SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER); 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 703adc57..25bb412a 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 @@ -34,7 +34,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { public static final String MEMBER_NUMBER_TAG = "M-"; private static Stringify stringify = stringify(HsOfficeMembershipEntity.class) - .withProp(HsOfficeMembershipEntity::getMemberNumberString) + .withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber()) .withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getMainDebitor().toShortString()) .withProp(e -> e.getValidity().asString()) @@ -91,25 +91,12 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { } return validity; } - - public String getMemberNumberString() { - return MEMBER_NUMBER_TAG + getMemberNumber(); - } - public Integer getMemberNumber() { - // TODO: refactor - String combinedMemberNumber; - if (partner.getPartnerNumber() == null ) { - if (memberNumberSuffix == null) { - combinedMemberNumber = null; - } else {combinedMemberNumber = MEMBER_NUMBER_TAG + memberNumberSuffix;} - } else if (memberNumberSuffix == null) { - combinedMemberNumber = partner.getPartnerNumber() + "??"; - } else { - combinedMemberNumber = partner.getPartnerNumber() + memberNumberSuffix; + if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) { + return null; } - return Optional.ofNullable(combinedMemberNumber).map(Integer::parseInt).orElse(null); + return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10); } @Override @@ -119,7 +106,7 @@ public class HsOfficeMembershipEntity implements HasUuid, Stringifyable { @Override public String toShortString() { - return "M-" + partner.getPartnerNumber() + String.valueOf(memberNumberSuffix); + return "M-" + getMemberNumber(); } @PrePersist diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java index 6de92ee8..c61a863e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepository.java @@ -28,13 +28,13 @@ public interface HsOfficeMembershipRepository extends Repository findMembershipsByPartnerNumberAndSuffix( + HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix( @NotNull Integer partnerNumber, @NotNull String suffix); - default List findMembershipsByMemberNumber(Integer memberNumber) { + default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) { final var partnerNumber = memberNumber / 100; final var suffix = memberNumber % 100; - return findMembershipsByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix)); + return findMembershipByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix)); } long count(); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java index 6c7a158c..dfbd1667 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepository.java @@ -23,6 +23,7 @@ public interface HsOfficePartnerRepository extends Repository findPartnerByOptionalNameLike(String name); + HsOfficePartnerEntity findPartnerByPartnerNumber(Integer partnerNumber); HsOfficePartnerEntity save(final HsOfficePartnerEntity entity); diff --git a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml index 855988b0..163f6f34 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-membership-schemas.yaml @@ -23,8 +23,15 @@ components: $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' mainDebitor: $ref: './hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor' - memberNumberSuffix: + memberNumber: type: integer + minimum: 1000000 + maximum: 9999999 + memberNumberSuffix: + type: string + minLength: 2 + maxLength: 2 + pattern: '[0-9]+' validFrom: type: string format: date @@ -67,7 +74,10 @@ components: format: uuid nullable: false memberNumberSuffix: - type: integer + type: string + minLength: 2 + maxLength: 2 + pattern: '[0-9]+' nullable: false validFrom: type: string @@ -84,6 +94,7 @@ components: type: boolean required: - partnerUuid + - memberNumberSuffix - mainDebitorUuid - validFrom - membershipFeeBillable diff --git a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml index c18ba861..3833752b 100644 --- a/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml +++ b/src/main/resources/api-definition/hs-office/hs-office-memberships.yaml @@ -1,6 +1,7 @@ get: summary: Returns a list of (optionally filtered) memberships. - description: Returns the list of (optionally filtered) memberships which are visible to the current user or any of it's assumed roles. + description: Returns the list of memberships which are visible to the current user or any of it's assumed roles. + The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time. tags: - hs-office-memberships operationId: listMemberships @@ -13,13 +14,13 @@ get: schema: type: string format: uuid - description: UUID of the business partner. - - name: memberNumberSuffix + description: UUID of the business partner, exclusive to `memberNumber`. + - name: memberNumber in: query required: false schema: type: integer - description: Member number. + description: Member number, exclusive to `partnerUuid`. responses: "200": description: OK diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index a7052a2e..4c17f8e1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -75,8 +75,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); RestAssured // @formatter:off .given() @@ -118,8 +117,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); RestAssured // @formatter:off .given() @@ -153,8 +151,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopAssetsTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var location = RestAssured // @formatter:off .given() @@ -199,8 +196,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreAssetsThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var location = RestAssured // @formatter:off .given() diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java index dfba6b7a..89f48402 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionRepositoryIntegrationTest.java @@ -62,8 +62,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopAssetsTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); // when final var result = attempt(em, () -> { @@ -96,7 +95,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var newCoopAssetsTransaction = HsOfficeCoopAssetsTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopAssetsTransactionType.DEPOSIT) @@ -159,7 +158,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(null).get(1000202); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); // when final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( @@ -179,8 +178,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase public void globalAdmin_canViewCoopAssetsTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); // when final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index ebb7ca59..58469681 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -75,7 +75,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid()).then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -109,7 +109,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202).get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" @@ -134,7 +134,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canAddCoopSharesTransaction() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" @@ -165,7 +165,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { void globalAdmin_canNotCancelMoreSharesThanCurrentlySubscribed() { context.define("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101).get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var location = RestAssured // @formatter:off .given().header("current-user", "superuser-alex@hostsharing.net").contentType(ContentType.JSON).body(""" diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java index 9ef64522..78d0ac7d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionRepositoryIntegrationTest.java @@ -61,8 +61,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // given context("superuser-alex@hostsharing.net"); final var count = coopSharesTransactionRepo.count(); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); // when final var result = attempt(em, () -> { @@ -95,8 +94,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // when attempt(em, () -> { - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000101); final var newCoopSharesTransaction = HsOfficeCoopSharesTransactionEntity.builder() .membership(givenMembership) .transactionType(HsOfficeCoopSharesTransactionType.SUBSCRIPTION) @@ -115,7 +113,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm view on coopsharestransaction#temprefB to role membership#10001:....tenant by system and assume }", + "{ grant perm view on coopsharestransaction#temprefB to role membership#1000101:....tenant by system and assume }", null)); } @@ -142,25 +140,24 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopSharesTransactionsAreReturned( result, - "CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)", - "CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)", - "CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)", + "CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)", + "CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)", + "CoopShareTransaction(1000101, 2022-10-20, ADJUSTMENT, 2, ref 1000101-3, some adjustment)", - "CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 4, ref 10002-1, initial subscription)", - "CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)", - "CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)", + "CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)", + "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)", + "CoopShareTransaction(1000202, 2022-10-20, ADJUSTMENT, 2, ref 1000202-3, some adjustment)", - "CoopShareTransaction(10003, 2010-03-15, SUBSCRIPTION, 4, ref 10003-1, initial subscription)", - "CoopShareTransaction(10003, 2021-09-01, CANCELLATION, -2, ref 10003-2, cancelling some)", - "CoopShareTransaction(10003, 2022-10-20, ADJUSTMENT, 2, ref 10003-3, some adjustment)"); + "CoopShareTransaction(1000303, 2010-03-15, SUBSCRIPTION, 4, ref 1000303-1, initial subscription)", + "CoopShareTransaction(1000303, 2021-09-01, CANCELLATION, -2, ref 1000303-2, cancelling some)", + "CoopShareTransaction(1000303, 2022-10-20, ADJUSTMENT, 2, ref 1000303-3, some adjustment)"); } @Test public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuid() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); // when final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( @@ -171,17 +168,16 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopSharesTransactionsAreReturned( result, - "CoopShareTransaction(10002, 2010-03-15, SUBSCRIPTION, 4, ref 10002-1, initial subscription)", - "CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)", - "CoopShareTransaction(10002, 2022-10-20, ADJUSTMENT, 2, ref 10002-3, some adjustment)"); + "CoopShareTransaction(1000202, 2010-03-15, SUBSCRIPTION, 4, ref 1000202-1, initial subscription)", + "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)", + "CoopShareTransaction(1000202, 2022-10-20, ADJUSTMENT, 2, ref 1000202-3, some adjustment)"); } @Test public void globalAdmin_canViewCoopSharesTransactions_filteredByMembershipUuidAndValueDateRange() { // given context("superuser-alex@hostsharing.net"); - final var givenMembership = membershipRepo.findMembershipsByMemberNumber(1000202) - .get(0); + final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); // when final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( @@ -192,7 +188,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // then allTheseCoopSharesTransactionsAreReturned( result, - "CoopShareTransaction(10002, 2021-09-01, CANCELLATION, -2, ref 10002-2, cancelling some)"); + "CoopShareTransaction(1000202, 2021-09-01, CANCELLATION, -2, ref 1000202-2, cancelling some)"); } @Test @@ -209,9 +205,9 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase // then: exactlyTheseCoopSharesTransactionsAreReturned( result, - "CoopShareTransaction(10001, 2010-03-15, SUBSCRIPTION, 4, ref 10001-1, initial subscription)", - "CoopShareTransaction(10001, 2021-09-01, CANCELLATION, -2, ref 10001-2, cancelling some)", - "CoopShareTransaction(10001, 2022-10-20, ADJUSTMENT, 2, ref 10001-3, some adjustment)"); + "CoopShareTransaction(1000101, 2010-03-15, SUBSCRIPTION, 4, ref 1000101-1, initial subscription)", + "CoopShareTransaction(1000101, 2021-09-01, CANCELLATION, -2, ref 1000101-2, cancelling some)", + "CoopShareTransaction(1000101, 2022-10-20, ADJUSTMENT, 2, ref 1000101-3, some adjustment)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java index c1cb6c18..6e3e8db1 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityUnitTest.java @@ -29,7 +29,7 @@ class HsOfficeDebitorEntityUnitTest { final var result = given.toString(); - assertThat(result).isEqualTo("debitor(1234567: LEGAL some trade name: som)"); + assertThat(result).isEqualTo("debitor(D-1234567: LEGAL some trade name: som)"); } @Test @@ -46,7 +46,7 @@ class HsOfficeDebitorEntityUnitTest { final var result = given.toString(); - assertThat(result).isEqualTo("debitor(1234567: )"); + assertThat(result).isEqualTo("debitor(D-1234567: )"); } @Test @@ -60,6 +60,60 @@ class HsOfficeDebitorEntityUnitTest { final var result = given.toShortString(); - assertThat(result).isEqualTo("1234567"); + assertThat(result).isEqualTo("D-1234567"); + } + + @Test + void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() { + final var given = HsOfficeDebitorEntity.builder() + .partner(HsOfficePartnerEntity.builder() + .partnerNumber(12345) + .build()) + .debitorNumberSuffix((byte)67) + .build(); + + final var result = given.getDebitorNumber(); + + assertThat(result).isEqualTo(1234567); + } + + @Test + void getDebitorNumberWithoutPartnerReturnsNull() { + final var given = HsOfficeDebitorEntity.builder() + .partner(null) + .debitorNumberSuffix((byte)67) + .build(); + + final var result = given.getDebitorNumber(); + + assertThat(result).isNull(); + } + + @Test + void getDebitorNumberWithoutPartnerNumberReturnsNull() { + final var given = HsOfficeDebitorEntity.builder() + .partner(HsOfficePartnerEntity.builder() + .partnerNumber(null) + .build()) + .debitorNumberSuffix((byte)67) + .build(); + + final var result = given.getDebitorNumber(); + + assertThat(result).isNull(); + } + + @Test + void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() { + final var given = HsOfficeDebitorEntity.builder() + .partner(HsOfficePartnerEntity.builder() + .partnerNumber(12345) + .build()) + .debitorNumberSuffix(null) + .build(); + + final var result = given.getDebitorNumber(); + + assertThat(result).isNull(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java index 97eb49b1..3e62168f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorRepositoryIntegrationTest.java @@ -211,9 +211,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then allTheseDebitorsAreReturned( result, - "debitor(1000111: LEGAL First GmbH: fir)", - "debitor(1000212: LEGAL Second e.K.: sec)", - "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); + "debitor(D-1000111: LEGAL First GmbH: fir)", + "debitor(D-1000212: LEGAL Second e.K.: sec)", + "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)"); } @ParameterizedTest @@ -231,8 +231,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { // then: exactlyTheseDebitorsAreReturned(result, - "debitor(1000111: LEGAL First GmbH: fir)", - "debitor(1000120: LEGAL First GmbH: fif)"); + "debitor(D-1000111: LEGAL First GmbH: fir)", + "debitor(D-1000120: LEGAL First GmbH: fif)"); } @Test @@ -260,7 +260,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByDebitorNumber(1000313); // then - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); + exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)"); } } @@ -276,7 +276,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest { final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); // then - exactlyTheseDebitorsAreReturned(result, "debitor(1000313: SOLE_REPRESENTATION Third OHG: thi)"); + exactlyTheseDebitorsAreReturned(result, "debitor(D-1000313: SOLE_REPRESENTATION Third OHG: thi)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 8fe2028a..0a96b732 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -61,7 +61,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @PersistenceContext EntityManager em; - private static int tempMemberNumberSuffix = 10; + private static int tempMemberNumberSuffix = 90; // TODO: check if we even need multiple distinct values @Nested @Accepts({ "Membership:F(Find)" }) @@ -84,7 +84,8 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "First GmbH" } }, "mainDebitor": { "debitorNumber": 1000111 }, - "memberNumberSuffix": 10001, + "memberNumber": 1000101, + "memberNumberSuffix": "01", "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -92,7 +93,8 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "Second e.K." } }, "mainDebitor": { "debitorNumber": 1000212 }, - "memberNumberSuffix": 10002, + "memberNumber": 1000202, + "memberNumberSuffix": "02", "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -100,7 +102,8 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "Third OHG" } }, "mainDebitor": { "debitorNumber": 1000313 }, - "memberNumberSuffix": 10003, + "memberNumber": 1000303, + "memberNumberSuffix": "03", "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -109,6 +112,67 @@ class HsOfficeMembershipControllerAcceptanceTest { """)); // @formatter:on } + + @Test + void globalAdmin_canViewMembershipsByPartnerUuid() throws JSONException { + + context.define("superuser-alex@hostsharing.net"); + final var partner = partnerRepo.findPartnerByPartnerNumber(10001); + + RestAssured // @formatter:off + .given() + .header("current-user", "superuser-alex@hostsharing.net") + .port(port) + .when() + .queryParam("partnerUuid", partner.getUuid() ) + .get("http://localhost/api/hs/office/memberships") + .then().log().all().assertThat() + .statusCode(200) + .contentType("application/json") + .body("", lenientlyEquals(""" + [ + { + "partner": { "person": { "tradeName": "First GmbH" } }, + "mainDebitor": { "debitorNumber": 1000111 }, + "memberNumber": 1000101, + "memberNumberSuffix": "01", + "validFrom": "2022-10-01", + "validTo": null, + "reasonForTermination": "NONE" + } + ] + """)); + // @formatter:on + } + + @Test + void globalAdmin_canViewMembershipsByMemberNumber() throws JSONException { + + RestAssured // @formatter:off + .given() + .header("current-user", "superuser-alex@hostsharing.net") + .port(port) + .when() + .queryParam("memberNumber", 1000202 ) + .get("http://localhost/api/hs/office/memberships") + .then().log().all().assertThat() + .statusCode(200) + .contentType("application/json") + .body("", lenientlyEquals(""" + [ + { + "partner": { "person": { "tradeName": "Second e.K." } }, + "mainDebitor": { "debitorNumber": 1000212 }, + "memberNumber": 1000202, + "memberNumberSuffix": "02", + "validFrom": "2022-10-01", + "validTo": null, + "reasonForTermination": "NONE" + } + ] + """)); + // @formatter:on + } } @Nested @@ -121,6 +185,9 @@ class HsOfficeMembershipControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); + final var givenMemberSuffixNumber = ++tempMemberNumberSuffix; + final var givenMemberSuffix = toPaddedSuffix(givenMemberSuffixNumber); + final var expectedMemberNumber = givenPartner.getPartnerNumber()*100+givenMemberSuffixNumber; final var location = RestAssured // @formatter:off .given() @@ -130,11 +197,11 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partnerUuid": "%s", "mainDebitorUuid": "%s", - "memberNumberSuffix": 20001, + "memberNumberSuffix": "%s", "validFrom": "2022-10-13", "membershipFeeBillable": "true" } - """.formatted(givenPartner.getUuid(), givenDebitor.getUuid())) + """.formatted(givenPartner.getUuid(), givenDebitor.getUuid(), givenMemberSuffix)) .port(port) .when() .post("http://localhost/api/hs/office/memberships") @@ -145,7 +212,8 @@ class HsOfficeMembershipControllerAcceptanceTest { .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) .body("mainDebitor.debitorNumberSuffix", is((int) givenDebitor.getDebitorNumberSuffix())) .body("partner.person.tradeName", is("Third OHG")) - .body("memberNumberSuffix", is(20001)) + .body("memberNumber", is(expectedMemberNumber)) + .body("memberNumberSuffix", is(givenMemberSuffix)) .body("validFrom", is("2022-10-13")) .body("validTo", equalTo(null)) .header("Location", startsWith("http://localhost")) @@ -166,9 +234,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Test void globalAdmin_canGetArbitraryMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0) - .getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid(); RestAssured // @formatter:off .given() @@ -183,7 +249,8 @@ class HsOfficeMembershipControllerAcceptanceTest { { "partner": { "person": { "tradeName": "First GmbH" } }, "mainDebitor": { "debitorNumber": 1000111 }, - "memberNumberSuffix": 10001, + "memberNumber": 1000101, + "memberNumberSuffix": "01", "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -195,9 +262,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void normalUser_canNotGetUnrelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000101) - .get(0) - .getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000101).getUuid(); RestAssured // @formatter:off .given() @@ -213,9 +278,7 @@ class HsOfficeMembershipControllerAcceptanceTest { @Accepts({ "Membership:X(Access Control)" }) void debitorAgentUser_canGetRelatedMembership() { context.define("superuser-alex@hostsharing.net"); - final var givenMembershipUuid = membershipRepo.findMembershipsByMemberNumber(1000303) - .get(0) - .getUuid(); + final var givenMembershipUuid = membershipRepo.findMembershipByMemberNumber(1000303).getUuid(); RestAssured // @formatter:off .given() @@ -234,7 +297,8 @@ class HsOfficeMembershipControllerAcceptanceTest { "debitorNumber": 1000313, "billingContact": { "label": "third contact" } }, - "memberNumberSuffix": 10003, + "memberNumber": 1000303, + "memberNumberSuffix": "03", "validFrom": "2022-10-01", "validTo": null, "reasonForTermination": "NONE" @@ -458,8 +522,10 @@ class HsOfficeMembershipControllerAcceptanceTest { void cleanup() { jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net", null); - final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '20'"); - query.executeUpdate(); - }); + final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '90'"); + if ( query.executeUpdate() > 0 ) { + query.toString(); + } + }).assertSuccessful(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java index 30617a36..63ea7306 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerRestTest.java @@ -6,7 +6,10 @@ import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.mapper.Mapper; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -22,6 +25,7 @@ import jakarta.persistence.SynchronizationType; import java.util.Map; import java.util.UUID; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -59,119 +63,166 @@ public class HsOfficeMembershipControllerRestTest { when(emf.createEntityManager(any(SynchronizationType.class), any(Map.class))).thenReturn(em); } - @Test - void respondBadRequest_ifPartnerUuidIsMissing() throws Exception { + @Nested + class AddMembership { + @Test + void respondBadRequest_ifPartnerUuidIsMissing() throws Exception { - // when - mockMvc.perform(MockMvcRequestBuilders - .post("/api/hs/office/memberships") - .header("current-user", "superuser-alex@hostsharing.net") - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "partnerUuid": null, - "mainDebitorUuid": "%s", - "memberNumberSuffix": 20001, - "validFrom": "2022-10-13", - "membershipFeeBillable": "true" - } - """.formatted(UUID.randomUUID())) - .accept(MediaType.APPLICATION_JSON)) + // when + mockMvc.perform(MockMvcRequestBuilders + .post("/api/hs/office/memberships") + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partnerUuid": null, + "mainDebitorUuid": "%s", + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """.formatted(UUID.randomUUID())) + .accept(MediaType.APPLICATION_JSON)) - // then - .andExpect(status().is4xxClientError()) - .andExpect(jsonPath("statusCode", is(400))) - .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]"))); + // then + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("statusCode", is(400))) + .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", is("[partnerUuid must not be null but is \"null\"]"))); + } + + @Test + void respondBadRequest_ifDebitorUuidIsMissing() throws Exception { + + // when + mockMvc.perform(MockMvcRequestBuilders + .post("/api/hs/office/memberships") + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partnerUuid": "%s", + "mainDebitorUuid": null, + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """.formatted(UUID.randomUUID())) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("statusCode", is(400))) + .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]"))); + } + + @Test + void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception { + + // given + final var givenPartnerUuid = UUID.randomUUID(); + final var givenMainDebitorUuid = UUID.randomUUID(); + when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null); + when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .post("/api/hs/office/memberships") + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partnerUuid": "%s", + "mainDebitorUuid": "%s", + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """.formatted(givenPartnerUuid, givenMainDebitorUuid)) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("statusCode", is(400))) + .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid))); + } + + @Test + void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception { + + // given + final var givenPartnerUuid = UUID.randomUUID(); + final var givenMainDebitorUuid = UUID.randomUUID(); + when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class)); + when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null); + + // when + mockMvc.perform(MockMvcRequestBuilders + .post("/api/hs/office/memberships") + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partnerUuid": "%s", + "mainDebitorUuid": "%s", + "memberNumberSuffix": "01", + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """.formatted(givenPartnerUuid, givenMainDebitorUuid)) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("statusCode", is(400))) + .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid))); + } + + @ParameterizedTest + @EnumSource(InvalidMemberSuffixVariants.class) + void respondBadRequest_ifMemberNumberSuffixIsInvalid(final InvalidMemberSuffixVariants testCase) throws Exception { + + // when + mockMvc.perform(MockMvcRequestBuilders + .post("/api/hs/office/memberships") + .header("current-user", "superuser-alex@hostsharing.net") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "partnerUuid": "%s", + "mainDebitorUuid": "%s", + %s + "validFrom": "2022-10-13", + "membershipFeeBillable": "true" + } + """.formatted(UUID.randomUUID(), UUID.randomUUID(), testCase.memberNumberSuffixEntry)) + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().is4xxClientError()) + .andExpect(jsonPath("statusCode", is(400))) + .andExpect(jsonPath("statusPhrase", is("Bad Request"))) + .andExpect(jsonPath("message", containsString(testCase.expectedErrorMessage))); + } + + public enum InvalidMemberSuffixVariants { + MISSING("", "[memberNumberSuffix must not be null but is \"null\"]"), + TOO_SMALL("\"memberNumberSuffix\": \"9\",", "memberNumberSuffix size must be between 2 and 2 but is \"9\""), + TOO_LARGE("\"memberNumberSuffix\": \"100\",", "memberNumberSuffix size must be between 2 and 2 but is \"100\""), + NOT_NUMERIC("\"memberNumberSuffix\": \"AA\",", "memberNumberSuffix must match \"[0-9]+\" but is \"AA\""), + EMPTY("\"memberNumberSuffix\": \"\",", "memberNumberSuffix size must be between 2 and 2 but is \"\""); + + private final String memberNumberSuffixEntry; + private final String expectedErrorMessage; + + InvalidMemberSuffixVariants(final String memberNumberSuffixEntry, final String expectedErrorMessage) { + this.memberNumberSuffixEntry = memberNumberSuffixEntry; + this.expectedErrorMessage = expectedErrorMessage; + } + } } - @Test - void respondBadRequest_ifDebitorUuidIsMissing() throws Exception { - - // when - mockMvc.perform(MockMvcRequestBuilders - .post("/api/hs/office/memberships") - .header("current-user", "superuser-alex@hostsharing.net") - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "partnerUuid": "%s", - "mainDebitorUuid": null, - "memberNumberSuffix": 20001, - "validFrom": "2022-10-13", - "membershipFeeBillable": "true" - } - """.formatted(UUID.randomUUID())) - .accept(MediaType.APPLICATION_JSON)) - - // then - .andExpect(status().is4xxClientError()) - .andExpect(jsonPath("statusCode", is(400))) - .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("[mainDebitorUuid must not be null but is \"null\"]"))); - } - - @Test - void respondBadRequest_ifAnyGivenPartnerUuidCannotBeFound() throws Exception { - - // given - final var givenPartnerUuid = UUID.randomUUID(); - final var givenMainDebitorUuid = UUID.randomUUID(); - when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(null); - when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(mock(HsOfficeDebitorEntity.class)); - - // when - mockMvc.perform(MockMvcRequestBuilders - .post("/api/hs/office/memberships") - .header("current-user", "superuser-alex@hostsharing.net") - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "partnerUuid": "%s", - "mainDebitorUuid": "%s", - "memberNumberSuffix": 20001, - "validFrom": "2022-10-13", - "membershipFeeBillable": "true" - } - """.formatted(givenPartnerUuid, givenMainDebitorUuid)) - .accept(MediaType.APPLICATION_JSON)) - - // then - .andExpect(status().is4xxClientError()) - .andExpect(jsonPath("statusCode", is(400))) - .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("Unable to find Partner with uuid " + givenPartnerUuid))); - } - - @Test - void respondBadRequest_ifAnyGivenDebitorUuidCannotBeFound() throws Exception { - - // given - final var givenPartnerUuid = UUID.randomUUID(); - final var givenMainDebitorUuid = UUID.randomUUID(); - when(em.find(HsOfficePartnerEntity.class, givenPartnerUuid)).thenReturn(mock(HsOfficePartnerEntity.class)); - when(em.find(HsOfficeDebitorEntity.class, givenMainDebitorUuid)).thenReturn(null); - - // when - mockMvc.perform(MockMvcRequestBuilders - .post("/api/hs/office/memberships") - .header("current-user", "superuser-alex@hostsharing.net") - .contentType(MediaType.APPLICATION_JSON) - .content(""" - { - "partnerUuid": "%s", - "mainDebitorUuid": "%s", - "memberNumberSuffix": 20001, - "validFrom": "2022-10-13", - "membershipFeeBillable": "true" - } - """.formatted(givenPartnerUuid, givenMainDebitorUuid)) - .accept(MediaType.APPLICATION_JSON)) - - // then - .andExpect(status().is4xxClientError()) - .andExpect(jsonPath("statusCode", is(400))) - .andExpect(jsonPath("statusPhrase", is("Bad Request"))) - .andExpect(jsonPath("message", is("Unable to find Debitor with uuid " + givenMainDebitorUuid))); - } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java index 9563d6ad..4132c1bd 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipEntityUnitTest.java @@ -1,6 +1,7 @@ package net.hostsharing.hsadminng.hs.office.membership; import com.vladmihalcea.hibernate.type.range.Range; +import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import org.junit.jupiter.api.Test; import jakarta.persistence.PrePersist; @@ -26,17 +27,49 @@ class HsOfficeMembershipEntityUnitTest { @Test void toStringContainsAllProps() { final var result = givenMembership.toString(); - - assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., 1000100, [2020-01-01,))"); + assertThat(result).isEqualTo("Membership(M-1000101, LEGAL Test Ltd., D-1000100, [2020-01-01,))"); } @Test void toShortStringContainsMemberNumberSuffixOnly() { final var result = givenMembership.toShortString(); - assertThat(result).isEqualTo("M-1000101"); } + @Test + void getMemberNumberWithPartnerAndSuffix() { + final var result = givenMembership.getMemberNumber(); + assertThat(result).isEqualTo(1000101); + } + + @Test + void getMemberNumberWithPartnerButWithoutSuffix() { + givenMembership.setMemberNumberSuffix(null); + final var result = givenMembership.getMemberNumber(); + assertThat(result).isEqualTo(null); + } + + @Test + void getMemberNumberWithoutPartnerButWithSuffix() { + givenMembership.setPartner(null); + final var result = givenMembership.getMemberNumber(); + assertThat(result).isEqualTo(null); + } + + @Test + void getMemberNumberWithoutPartnerNumberButWithSuffix() { + givenMembership.setPartner(HsOfficePartnerEntity.builder().build()); + final var result = givenMembership.getMemberNumber(); + assertThat(result).isEqualTo(null); + } + + @Test + void getValidtyIfNull() { + givenMembership.setValidity(null); + final var result = givenMembership.getValidity(); + assertThat(result).isEqualTo(Range.infinite(LocalDate.class)); + } + @Test void initializesReasonForTerminationInPrePersistIfNull() throws Exception { final var givenUninitializedMembership = new HsOfficeMembershipEntity(); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index e6ec5bb2..7570d31e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -184,9 +184,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // then exactlyTheseMembershipsAreReturned( result, - "Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)", - "Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)", - "Membership(M-1000303, SOLE_REPRESENTATION Third OHG, 1000313, [2022-10-01,), NONE)"); + "Membership(M-1000101, LEGAL First GmbH, D-1000111, [2022-10-01,), NONE)", + "Membership(M-1000202, LEGAL Second e.K., D-1000212, [2022-10-01,), NONE)", + "Membership(M-1000303, SOLE_REPRESENTATION Third OHG, D-1000313, [2022-10-01,), NONE)"); } @Test @@ -200,7 +200,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // then exactlyTheseMembershipsAreReturned(result, - "Membership(M-1000101, LEGAL First GmbH, 1000111, [2022-10-01,), NONE)"); + "Membership(M-1000101, LEGAL First GmbH, D-1000111, [2022-10-01,), NONE)"); } @Test @@ -209,11 +209,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { context("superuser-alex@hostsharing.net"); // when - final var result = membershipRepo.findMembershipsByMemberNumber(1000202); + final var result = membershipRepo.findMembershipByMemberNumber(1000202); // then - exactlyTheseMembershipsAreReturned(result, - "Membership(M-1000202, LEGAL Second e.K., 1000212, [2022-10-01,), NONE)"); + assertThat(result) + .isNotNull() + .extracting(Object::toString) + .isEqualTo("Membership(M-1000202, LEGAL Second e.K., D-1000212, [2022-10-01,), NONE)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index 18eb063d..3aba5640 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -211,6 +211,25 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { } } + @Nested + class FindByPartnerNumber { + + @Test + public void globalAdmin_withoutAssumedRole_canViewAllPartners() { + // given + context("superuser-alex@hostsharing.net"); + + // when + final var result = partnerRepo.findPartnerByPartnerNumber(10001); + + // then + assertThat(result) + .isNotNull() + .extracting(Object::toString) + .isEqualTo("partner(LEGAL First GmbH: first contact)"); + } + } + @Nested class UpdatePartner { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java new file mode 100644 index 00000000..f1246561 --- /dev/null +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java @@ -0,0 +1,32 @@ +package net.hostsharing.hsadminng.hs.office.relationship; + +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; +import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class HsOfficeRelationshipEntityUnitTest { + + private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.LEGAL) + .tradeName("some trade name") + .build(); + private HsOfficePersonEntity holder = HsOfficePersonEntity.builder() + .personType(HsOfficePersonType.NATURAL) + .familyName("Meier") + .givenName("Mellie") + .build(); + + @Test + void toShortString() { + final var given = HsOfficeRelationshipEntity.builder() + .relType(HsOfficeRelationshipType.REPRESENTATIVE) + .relAnchor(anchor) + .relHolder(holder) + .build(); + + assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LEGAL some trade name', relType='REPRESENTATIVE', relHolder='NATURAL Meier, Mellie')"); + } +} diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java index 70cf53da..f38a3f8f 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/sepamandate/HsOfficeSepaMandateControllerAcceptanceTest.java @@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); @@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest { // finally, the sepaMandate is actually updated assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() .matches(mandate -> { - assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: LEGAL First GmbH: fir)"); + assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(D-1000111: LEGAL First GmbH: fir)"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); -- 2.39.2 From cf4caf6e6617e76619fb6f85a474383f1b557310 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 12:50:11 +0100 Subject: [PATCH 67/72] rebasing aftermaths --- .../rbacuser/RbacUserControllerAcceptanceTest.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java index 9f2fd68e..b13bcb76 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/rbacuser/RbacUserControllerAcceptanceTest.java @@ -9,13 +9,10 @@ import net.hostsharing.test.JpaAttempt; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.transaction.annotation.Transactional; -import jakarta.persistence.EntityManager; -import jakarta.persistence.PersistenceContext; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; @@ -31,9 +28,6 @@ class RbacUserControllerAcceptanceTest { @LocalServerPort private Integer port; - @PersistenceContext - EntityManager em; - @Autowired JpaAttempt jpaAttempt; @@ -43,14 +37,6 @@ class RbacUserControllerAcceptanceTest { @Autowired RbacUserRepository rbacUserRepository; - @Value("${HSADMINNG_POSTGRES_RESTRICTED_USERNAME}") - String restrictedUser; - - @Test - void testEnv() { - assertThat(restrictedUser).isEqualTo("restricted"); - } - @Nested class CreateRbacUser { -- 2.39.2 From e304cf9bf4881cb036a5c6cc21afa13743c45a08 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 13:00:13 +0100 Subject: [PATCH 68/72] improved documentation --- .../HsOfficeCoopAssetsTransactionEntity.java | 13 +++++ .../HsOfficeCoopAssetsTransactionType.java | 47 +++++++++++++++---- .../HsOfficeCoopSharesTransactionEntity.java | 8 ++++ .../HsOfficeCoopSharesTransactionType.java | 17 +++++-- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java index f0b91c07..e91bc8bd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionEntity.java @@ -53,12 +53,25 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUu @Column(name = "valuedate") private LocalDate valueDate; + /** + * The signed value which directly affects the booking balance. + * + *

This means, that a DEPOSIT is always positive, a DISBURSAL is always negative, + * but an ADJUSTMENT can bei either positive or negative. + * See {@link HsOfficeCoopAssetsTransactionType} for

more information. + */ @Column(name = "assetvalue") private BigDecimal assetValue; + /** + * The booking reference. + */ @Column(name = "reference") private String reference; + /** + * An optional arbitrary comment. + */ @Column(name = "comment") private String comment; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java index 2245f864..d7397622 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionType.java @@ -1,12 +1,43 @@ package net.hostsharing.hsadminng.hs.office.coopassets; public enum HsOfficeCoopAssetsTransactionType { - ADJUSTMENT, // correction of wrong bookings - DEPOSIT, // payment received from member after signing shares, >0 - DISBURSAL, // payment send to member after cancellation of shares, <0 - TRANSFER, // transferring shares to another member, <0 - ADOPTION, // receiving shares from another member, >0 - CLEARING, // settlement with members dept, <0 - LOSS, // assignment of balance sheet loss in case of cancellation of shares, <0 - LIMITATION // limitation period was reached after impossible disbursal, <0 + /** + * correction of wrong bookings, value can be positive or negative + */ + ADJUSTMENT, + + /** + * payment received from member after signing shares, value >0 + */ + DEPOSIT, + + /** + * payment send to member after cancellation of shares, value <0 + */ + DISBURSAL, + + /** + * transferring shares to another member, value <0 + */ + TRANSFER, + + /** + * receiving shares from another member, value >0 + */ + ADOPTION, + + /** + * settlement with members dept, value <0 + */ + CLEARING, + + /** + * assignment of balance sheet loss in case of cancellation of shares, value <0 + */ + LOSS, + + /** + * limitation period was reached after impossible disbursal, value <0 + */ + LIMITATION } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 749b616c..22cecc55 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -2,6 +2,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; +import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; @@ -46,6 +47,13 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu @Enumerated(EnumType.STRING) private HsOfficeCoopSharesTransactionType transactionType; + /** + * The signed value which directly affects the booking balance. + * + *

This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative, + * but an ADJUSTMENT can bei either positive or negative. + * See {@link HsOfficeCoopSharesTransactionType} for

more information. + */ @Column(name = "valuedate") private LocalDate valueDate; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java index e18451f5..bcd46cb3 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionType.java @@ -1,7 +1,18 @@ package net.hostsharing.hsadminng.hs.office.coopshares; public enum HsOfficeCoopSharesTransactionType { - ADJUSTMENT, // correction of wrong bookings - SUBSCRIPTION, // shares signed, e.g. with the declaration of accession, >0 - CANCELLATION; // shares terminated, e.g. when a membership is resigned, <0 + /** + * correction of wrong bookings, with either positive or negative value + */ + ADJUSTMENT, + + /** + * shares signed, e.g. with the declaration of accession, value >0 + */ + SUBSCRIPTION, + + /** + * shares terminated, e.g. when a membership is resigned, value <0 + */ + CANCELLATION; } -- 2.39.2 From c70705a2e6e914822ee76b9233e88b75dfa4b241 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 13:23:21 +0100 Subject: [PATCH 69/72] TODO cleanup --- .../HsOfficeCoopSharesTransactionEntity.java | 3 +- .../membership/HsOfficeMembershipEntity.java | 1 - .../changelog/223-hs-office-partner-rbac.sql | 1 - .../318-hs-office-coopshares-test-data.sql | 2 +- ...ceCoopSharesTransactionEntityUnitTest.java | 4 +-- ...iceMembershipControllerAcceptanceTest.java | 26 ++++++--------- .../hs/office/migration/ImportOfficeData.java | 32 +++++++++---------- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index 22cecc55..e2ac5e6f 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.coopshares; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; -import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.migration.HasUuid; import net.hostsharing.hsadminng.stringify.Stringify; @@ -77,6 +76,6 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu @Override public String toShortString() { - return "%s%+d".formatted(getMemberNumber(), shareCount); + return "M-%s%+d".formatted(getMemberNumber(), shareCount); } } 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 25bb412a..355b79a9 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 @@ -15,7 +15,6 @@ import org.hibernate.annotations.Type; import jakarta.persistence.*; import java.time.LocalDate; -import java.util.Optional; import java.util.UUID; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; diff --git a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql index 852ed8ad..97079877 100644 --- a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql @@ -165,7 +165,6 @@ execute procedure hsOfficePartnerRbacRolesTrigger(); --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityView('hs_office_partner', $idName$ - -- TODO: simplify by using just partnerNumber for the essential part partnerNumber || ':' || (select idName from hs_office_person_iv p where p.uuid = target.personuuid) || '-' || diff --git a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql index 2bef88af..c3d2bf98 100644 --- a/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql +++ b/src/main/resources/db/changelog/318-hs-office-coopshares-test-data.sql @@ -10,7 +10,7 @@ */ create or replace procedure createHsOfficeCoopSharesTransactionTestData( givenPartnerNumber numeric, - givenMemberNumberSuffix varchar -- TODO char(2)? + givenMemberNumberSuffix char(2) ) language plpgsql as $$ declare diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java index 862c593e..0170e1d8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntityUnitTest.java @@ -29,7 +29,7 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { void toShortStringContainsOnlyMemberNumberAndShareCountOnly() { final var result = givenCoopSharesTransaction.toShortString(); - assertThat(result).isEqualTo("1000101+4"); + assertThat(result).isEqualTo("M-1000101+4"); } @Test @@ -43,6 +43,6 @@ class HsOfficeCoopSharesTransactionEntityUnitTest { void toShortStringEmptyTransactionDoesNotThrowException() { final var result = givenEmptyCoopSharesTransaction.toShortString(); - assertThat(result).isEqualTo("null+0"); + assertThat(result).isEqualTo("M-null+0"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java index 0a96b732..c46fe77e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipControllerAcceptanceTest.java @@ -11,7 +11,6 @@ import net.hostsharing.test.Accepts; import net.hostsharing.test.JpaAttempt; import org.json.JSONException; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +36,8 @@ import static org.hamcrest.Matchers.*; @Transactional class HsOfficeMembershipControllerAcceptanceTest { + private static String TEMP_MEMBER_NUMBER_SUFFIX = "90"; + @LocalServerPort private Integer port; @@ -61,8 +62,6 @@ class HsOfficeMembershipControllerAcceptanceTest { @PersistenceContext EntityManager em; - private static int tempMemberNumberSuffix = 90; // TODO: check if we even need multiple distinct values - @Nested @Accepts({ "Membership:F(Find)" }) class ListMemberships { @@ -185,9 +184,8 @@ class HsOfficeMembershipControllerAcceptanceTest { context.define("superuser-alex@hostsharing.net"); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Third").get(0); final var givenDebitor = debitorRepo.findDebitorByOptionalNameLike("Third").get(0); - final var givenMemberSuffixNumber = ++tempMemberNumberSuffix; - final var givenMemberSuffix = toPaddedSuffix(givenMemberSuffixNumber); - final var expectedMemberNumber = givenPartner.getPartnerNumber()*100+givenMemberSuffixNumber; + final var givenMemberSuffix = TEMP_MEMBER_NUMBER_SUFFIX; + final var expectedMemberNumber = Integer.parseInt(givenPartner.getPartnerNumber() + TEMP_MEMBER_NUMBER_SUFFIX); final var location = RestAssured // @formatter:off .given() @@ -503,7 +501,7 @@ class HsOfficeMembershipControllerAcceptanceTest { .uuid(UUID.randomUUID()) .partner(givenPartner) .mainDebitor(givenDebitor) - .memberNumberSuffix(toPaddedSuffix(++tempMemberNumberSuffix)) + .memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .reasonForTermination(NONE) .membershipFeeBillable(true) @@ -513,19 +511,15 @@ class HsOfficeMembershipControllerAcceptanceTest { }).assertSuccessful().returnedValue(); } - private String toPaddedSuffix(final int numericSuffix) { - return String.format("%02d", numericSuffix); - } - - @BeforeEach @AfterEach void cleanup() { jpaAttempt.transacted(() -> { context.define("superuser-alex@hostsharing.net", null); - final var query = em.createQuery("DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '90'"); - if ( query.executeUpdate() > 0 ) { - query.toString(); - } + final var query = em.createQuery( + "DELETE FROM HsOfficeMembershipEntity m WHERE m.memberNumberSuffix >= '%s'" + .formatted(TEMP_MEMBER_NUMBER_SUFFIX) + ); + query.executeUpdate(); }).assertSuccessful(); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java index 473807eb..78f3c95e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java @@ -176,15 +176,15 @@ public class ImportOfficeData extends ContextBasedTest { assertThat(toFormattedString(contacts)).isEqualTo("{}"); assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" { - 17=debitor(1001700: null null, null: mih), - 20=debitor(1002000: null null, null: xyz), - 22=debitor(1102200: null null, null: xxx)} + 17=debitor(D-1001700: null null, null: mih), + 20=debitor(D-1002000: null null, null: xyz), + 22=debitor(D-1102200: null null, null: xxx)} """); assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { - 17=Membership(1001700, null null, null, 1001700, [2000-12-06,), NONE), - 20=Membership(1002000, null null, null, 1002000, [2000-12-06,2016-01-01), UNKNOWN), - 22=Membership(1102200, null null, null, 1102200, [2021-04-01,), NONE) + 17=Membership(M-1001700, null null, null, D-1001700, [2000-12-06,), NONE), + 20=Membership(M-1002000, null null, null, D-1002000, [2000-12-06,2016-01-01), UNKNOWN), + 22=Membership(M-1102200, null null, null, D-1102200, [2021-04-01,), NONE) } """); } @@ -235,16 +235,16 @@ public class ImportOfficeData extends ContextBasedTest { """); assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" { - 17=debitor(1001700: NATURAL Mellies, Michael: mih), - 20=debitor(1002000: LEGAL JM GmbH: xyz), - 22=debitor(1102200: LEGAL Test PS: xxx) + 17=debitor(D-1001700: NATURAL Mellies, Michael: mih), + 20=debitor(D-1002000: LEGAL JM GmbH: xyz), + 22=debitor(D-1102200: LEGAL Test PS: xxx) } """); assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" { - 17=Membership(10017, NATURAL Mellies, Michael, 1001700, [2000-12-06,), NONE), - 20=Membership(10020, LEGAL JM GmbH, 1002000, [2000-12-06,2016-01-01), UNKNOWN), - 22=Membership(11022, LEGAL Test PS, 1102200, [2021-04-01,), NONE) + 17=Membership(M-1001700, NATURAL Mellies, Michael, D-1001700, [2000-12-06,), NONE), + 20=Membership(M-1002000, LEGAL JM GmbH, D-1002000, [2000-12-06,2016-01-01), UNKNOWN), + 22=Membership(M-1102200, LEGAL Test PS, D-1102200, [2021-04-01,), NONE) } """); assertThat(toFormattedString(relationships)).isEqualToIgnoringWhitespace(""" @@ -312,10 +312,10 @@ public class ImportOfficeData extends ContextBasedTest { assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace(""" { - 33443=CoopShareTransaction(10017, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), - 33451=CoopShareTransaction(10020, 2000-12-06, SUBSCRIPTION, 2, initial share subscription), - 33701=CoopShareTransaction(10017, 2005-01-10, SUBSCRIPTION, 40, increase), - 33810=CoopShareTransaction(10020, 2016-12-31, CANCELLATION, 22, membership ended) + 33443=CoopShareTransaction(M-1001700, 2000-12-06, SUBSCRIPTION, 20, initial share subscription), + 33451=CoopShareTransaction(M-1002000, 2000-12-06, SUBSCRIPTION, 2, initial share subscription), + 33701=CoopShareTransaction(M-1001700, 2005-01-10, SUBSCRIPTION, 40, increase), + 33810=CoopShareTransaction(M-1002000, 2016-12-31, CANCELLATION, 22, membership ended) } """); } -- 2.39.2 From 06657f26bb2a2d8866648778fca32b9f488c8e61 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 13:24:53 +0100 Subject: [PATCH 70/72] improve documentation --- .../coopshares/HsOfficeCoopSharesTransactionEntity.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index e2ac5e6f..a13354e4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -59,9 +59,15 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu @Column(name = "sharecount") private int shareCount; + /** + * The ooking reference. + */ @Column(name = "reference") private String reference; + /** + * An optional arbitrary comment. + */ @Column(name = "comment") private String comment; -- 2.39.2 From 4ca89df6fd1988454c966442b91a8420d23c3046 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 15:49:08 +0100 Subject: [PATCH 71/72] amendmends according to code review and rebasing aftermaths --- .../coopshares/HsOfficeCoopSharesTransactionEntity.java | 2 +- .../resources/db/changelog/223-hs-office-partner-rbac.sql | 1 - .../resources/db/changelog/300-hs-office-membership.sql | 4 +++- ...fficeCoopAssetsTransactionControllerAcceptanceTest.java | 4 ++-- ...fficeCoopSharesTransactionControllerAcceptanceTest.java | 7 ++++--- .../debitor/HsOfficeDebitorEntityPatcherUnitTest.java | 6 ------ .../HsOfficeMembershipRepositoryIntegrationTest.java | 2 +- .../partner/HsOfficePartnerRepositoryIntegrationTest.java | 2 +- .../relationship/HsOfficeRelationshipEntityUnitTest.java | 6 +++--- 9 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java index a13354e4..f6a05bc4 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionEntity.java @@ -60,7 +60,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUu private int shareCount; /** - * The ooking reference. + * The Booking reference. */ @Column(name = "reference") private String reference; diff --git a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql index 97079877..5757efc9 100644 --- a/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql +++ b/src/main/resources/db/changelog/223-hs-office-partner-rbac.sql @@ -179,7 +179,6 @@ call generateRbacIdentityView('hs_office_partner', $idName$ call generateRbacRestrictedView('hs_office_partner', '(select idName from hs_office_person_iv p where p.uuid = target.personUuid)', $updates$ - partnerNumber = new.partnerNumber, personUuid = new.personUuid, contactUuid = new.contactUuid $updates$); diff --git a/src/main/resources/db/changelog/300-hs-office-membership.sql b/src/main/resources/db/changelog/300-hs-office-membership.sql index 01e644cd..acc0651a 100644 --- a/src/main/resources/db/changelog/300-hs-office-membership.sql +++ b/src/main/resources/db/changelog/300-hs-office-membership.sql @@ -17,7 +17,9 @@ create table if not exists hs_office_membership memberNumberSuffix::text ~ '^[0-9][0-9]$'), validity daterange not null, reasonForTermination HsOfficeReasonForTermination not null default 'NONE', - membershipFeeBillable boolean not null default true + membershipFeeBillable boolean not null default true, + + UNIQUE(partnerUuid, memberNumberSuffix) ); --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java index 4c17f8e1..b5dfa429 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerAcceptanceTest.java @@ -72,7 +72,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffix() { + void globalAdmin_canFindCoopAssetsTransactionsByMemberNumber() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); @@ -114,7 +114,7 @@ class HsOfficeCoopAssetsTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopAssetsTransactionsByMemberNumberSuffixAndDateRange() { + void globalAdmin_canFindCoopAssetsTransactionsByMembershipUuidAndDateRange() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java index 58469681..787fe467 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopshares/HsOfficeCoopSharesTransactionControllerAcceptanceTest.java @@ -72,7 +72,7 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffix() { + void globalAdmin_canFindCoopSharesTransactionsByMemberNumber() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); @@ -106,13 +106,14 @@ class HsOfficeCoopSharesTransactionControllerAcceptanceTest { } @Test - void globalAdmin_canFindCoopSharesTransactionsByMemberNumberSuffixAndDateRange() { + void globalAdmin_canFindCoopSharesTransactionsByMembershipUuidAndDateRange() { context.define("superuser-alex@hostsharing.net"); final var givenMembership = membershipRepo.findMembershipByMemberNumber(1000202); RestAssured // @formatter:off - .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when().get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" + .given().header("current-user", "superuser-alex@hostsharing.net").port(port).when() + .get("http://localhost/api/hs/office/coopsharestransactions?membershipUuid=" + givenMembership.getUuid() + "&fromValueDate=2020-01-01&toValueDate=2021-12-31").then().log().all().assertThat().statusCode(200).contentType("application/json").body("", lenientlyEquals(""" [ { "transactionType": "CANCELLATION", diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java index d6384e21..01ea5777 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/debitor/HsOfficeDebitorEntityPatcherUnitTest.java @@ -104,12 +104,6 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase< HsOfficeDebitorEntity::setBillingContact, newBillingContact(PATCHED_CONTACT_UUID)) .notNullable(), - new SimpleProperty<>( - "personType", - HsOfficeDebitorPatchResource::setBillable, - PATCHED_BILLABLE, - HsOfficeDebitorEntity::setBillable) - .notNullable(), new SimpleProperty<>( "billable", HsOfficeDebitorPatchResource::setBillable, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java index 632e8690..af62541c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/membership/HsOfficeMembershipRepositoryIntegrationTest.java @@ -77,7 +77,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest { // when final var result = attempt(em, () -> { final var newMembership = toCleanup(HsOfficeMembershipEntity.builder() - .memberNumberSuffix("01") + .memberNumberSuffix("11") .partner(givenPartner) .mainDebitor(givenDebitor) .validity(Range.closedInfinite(LocalDate.parse("2020-01-01"))) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java index 772bb640..f764163d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerRepositoryIntegrationTest.java @@ -226,7 +226,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest { assertThat(result) .isNotNull() .extracting(Object::toString) - .isEqualTo("partner(LEGAL First GmbH: first contact)"); + .isEqualTo("partner(LP First GmbH: first contact)"); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java index f1246561..45049ef8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/relationship/HsOfficeRelationshipEntityUnitTest.java @@ -10,11 +10,11 @@ import static org.junit.jupiter.api.Assertions.*; class HsOfficeRelationshipEntityUnitTest { private HsOfficePersonEntity anchor = HsOfficePersonEntity.builder() - .personType(HsOfficePersonType.LEGAL) + .personType(HsOfficePersonType.LEGAL_PERSON) .tradeName("some trade name") .build(); private HsOfficePersonEntity holder = HsOfficePersonEntity.builder() - .personType(HsOfficePersonType.NATURAL) + .personType(HsOfficePersonType.NATURAL_PERSON) .familyName("Meier") .givenName("Mellie") .build(); @@ -27,6 +27,6 @@ class HsOfficeRelationshipEntityUnitTest { .relHolder(holder) .build(); - assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LEGAL some trade name', relType='REPRESENTATIVE', relHolder='NATURAL Meier, Mellie')"); + assertThat(given.toShortString()).isEqualTo("rel(relAnchor='LP some trade name', relType='REPRESENTATIVE', relHolder='NP Meier, Mellie')"); } } -- 2.39.2 From d34e1f9e142e0b24cec859d4b7cb829f04234f0f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 24 Jan 2024 15:56:34 +0100 Subject: [PATCH 72/72] improve test coverage --- .../person/HsOfficePersonTypeConverterUnitTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonTypeConverterUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonTypeConverterUnitTest.java index 93283f2b..1077d462 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonTypeConverterUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/person/HsOfficePersonTypeConverterUnitTest.java @@ -1,5 +1,6 @@ package net.hostsharing.hsadminng.hs.office.person; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -15,10 +16,19 @@ class HsOfficePersonTypeConverterUnitTest { assertThat(converter.convertToDatabaseColumn(given)).isEqualTo(given.shortName); } + @Test + void mapsNullToDatabaseValue() { + assertThat(converter.convertToDatabaseColumn(null)).isEqualTo(null); + } @ParameterizedTest @EnumSource(HsOfficePersonType.class) void mapsFromDatabaseValue(final HsOfficePersonType given) { assertThat(converter.convertToEntityAttribute(given.shortName)).isEqualTo(given); } + + @Test + void mapsNullFromDatabaseValue() { + assertThat(converter.convertToEntityAttribute(null)).isEqualTo(null); + } } -- 2.39.2