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; + } + }