import cancelled memberships if shares or asset booking exists

This commit is contained in:
Michael Hoennig 2024-04-08 19:45:41 +02:00
parent ec1deb8903
commit 367951c93d
19 changed files with 191 additions and 119 deletions

View File

@ -19,13 +19,7 @@ import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.Column; import jakarta.persistence.*;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;

View File

@ -2,7 +2,11 @@ package net.hostsharing.hsadminng.hs.office.membership;
import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType; import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType;
import io.hypersistence.utils.hibernate.type.range.Range; import io.hypersistence.utils.hibernate.type.range.Range;
import lombok.*; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
@ -13,17 +17,32 @@ import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import jakarta.persistence.*; import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Pattern;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static io.hypersistence.utils.hibernate.type.range.Range.emptyRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
@ -50,7 +69,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
.withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber()) .withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber())
.withProp(e -> e.getPartner().toShortString()) .withProp(e -> e.getPartner().toShortString())
.withProp(e -> e.getValidity().asString()) .withProp(e -> e.getValidity().asString())
.withProp(HsOfficeMembershipEntity::getReasonForTermination) .withProp(HsOfficeMembershipEntity::getStatus)
.quotedValues(false); .quotedValues(false);
@Id @Id
@ -75,9 +94,9 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
@Column(name = "membershipfeebillable", nullable = false) @Column(name = "membershipfeebillable", nullable = false)
private Boolean membershipFeeBillable; // not primitive to force setting the value private Boolean membershipFeeBillable; // not primitive to force setting the value
@Column(name = "reasonfortermination") @Column(name = "status")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private HsOfficeReasonForTermination reasonForTermination; private HsOfficeMembershipStatus status;
public void setValidFrom(final LocalDate validFrom) { public void setValidFrom(final LocalDate validFrom) {
setValidity(toPostgresDateRange(validFrom, getValidTo())); setValidity(toPostgresDateRange(validFrom, getValidTo()));
@ -97,7 +116,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
public Range<LocalDate> getValidity() { public Range<LocalDate> getValidity() {
if (validity == null) { if (validity == null) {
validity = Range.infinite(LocalDate.class); validity = emptyRange(LocalDate.class);
} }
return validity; return validity;
} }
@ -121,8 +140,8 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
@PrePersist @PrePersist
void init() { void init() {
if (getReasonForTermination() == null) { if (getStatus() == null) {
setReasonForTermination(HsOfficeReasonForTermination.NONE); setStatus(HsOfficeMembershipStatus.INVALID);
} }
} }
@ -135,7 +154,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid
""")) """))
.withRestrictedViewOrderBy(SQL.projection("validity")) .withRestrictedViewOrderBy(SQL.projection("validity"))
.withUpdatableColumns("validity", "membershipFeeBillable", "reasonForTermination") .withUpdatableColumns("validity", "membershipFeeBillable", "status")
.importEntityAlias("partnerRel", HsOfficeRelationEntity.class, .importEntityAlias("partnerRel", HsOfficeRelationEntity.class,
dependsOnColumn("partnerUuid"), dependsOnColumn("partnerUuid"),

View File

@ -23,9 +23,9 @@ public class HsOfficeMembershipEntityPatcher implements EntityPatcher<HsOfficeMe
public void apply(final HsOfficeMembershipPatchResource resource) { public void apply(final HsOfficeMembershipPatchResource resource) {
OptionalFromJson.of(resource.getValidTo()).ifPresent( OptionalFromJson.of(resource.getValidTo()).ifPresent(
entity::setValidTo); entity::setValidTo);
Optional.ofNullable(resource.getReasonForTermination()) Optional.ofNullable(resource.getStatus())
.map(v -> mapper.map(v, HsOfficeReasonForTermination.class)) .map(v -> mapper.map(v, HsOfficeMembershipStatus.class))
.ifPresent(entity::setReasonForTermination); .ifPresent(entity::setStatus);
OptionalFromJson.of(resource.getMembershipFeeBillable()).ifPresent( OptionalFromJson.of(resource.getMembershipFeeBillable()).ifPresent(
entity::setMembershipFeeBillable); entity::setMembershipFeeBillable);
} }

View File

@ -0,0 +1,5 @@
package net.hostsharing.hsadminng.hs.office.membership;
public enum HsOfficeMembershipStatus {
INVALID, ACTIVE, CANCELLED, TRANSFERRED, DECEASED, LIQUIDATED, EXPULSED, UNKNOWN;
}

View File

@ -1,5 +0,0 @@
package net.hostsharing.hsadminng.hs.office.membership;
public enum HsOfficeReasonForTermination {
NONE, CANCELLATION, TRANSFER, DEATH, LIQUIDATION, EXPULSION, UNKNOWN;
}

View File

@ -3,15 +3,17 @@ components:
schemas: schemas:
HsOfficeReasonForTermination: HsOfficeMembershipStatus:
type: string type: string
enum: enum:
- NONE - INVALID
- CANCELLATION - ACTIVE
- TRANSFER - CANCELLED
- DEATH - TRANSFERRED
- LIQUIDATION - DECEASED
- EXPULSION - LIQUIDATED
- EXPULSED
- UNKNOWN
HsOfficeMembership: HsOfficeMembership:
type: object type: object
@ -38,8 +40,8 @@ components:
validTo: validTo:
type: string type: string
format: date format: date
reasonForTermination: status:
$ref: '#/components/schemas/HsOfficeReasonForTermination' $ref: '#/components/schemas/HsOfficeMembershipStatus'
membershipFeeBillable: membershipFeeBillable:
type: boolean type: boolean
@ -50,9 +52,8 @@ components:
type: string type: string
format: date format: date
nullable: true nullable: true
reasonForTermination: status:
nullable: true $ref: '#/components/schemas/HsOfficeMembershipStatus'
$ref: '#/components/schemas/HsOfficeReasonForTermination'
membershipFeeBillable: membershipFeeBillable:
nullable: true nullable: true
type: boolean type: boolean
@ -79,8 +80,8 @@ components:
type: string type: string
format: date format: date
nullable: true nullable: true
reasonForTermination: status:
$ref: '#/components/schemas/HsOfficeReasonForTermination' $ref: '#/components/schemas/HsOfficeMembershipStatus'
membershipFeeBillable: membershipFeeBillable:
nullable: false nullable: false
type: boolean type: boolean

View File

@ -1,4 +1,4 @@
openapi: 3.0.1 openapi: 3.0.3
info: info:
title: Hostsharing hsadmin-ng API title: Hostsharing hsadmin-ng API
version: v0 version: v0

View File

@ -4,9 +4,18 @@
--changeset hs-office-membership-MAIN-TABLE:1 endDelimiter:--// --changeset hs-office-membership-MAIN-TABLE:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
CREATE TYPE HsOfficeReasonForTermination AS ENUM ('NONE', 'CANCELLATION', 'TRANSFER', 'DEATH', 'LIQUIDATION', 'EXPULSION', 'UNKNOWN'); CREATE TYPE HsOfficeMembershipStatus AS ENUM (
'INVALID',
'ACTIVE',
'CANCELLED',
'TRANSFERRED',
'DECEASED',
'LIQUIDATED',
'EXPULSED',
'UNKNOWN'
);
CREATE CAST (character varying as HsOfficeReasonForTermination) WITH INOUT AS IMPLICIT; CREATE CAST (character varying as HsOfficeMembershipStatus) WITH INOUT AS IMPLICIT;
create table if not exists hs_office_membership create table if not exists hs_office_membership
( (
@ -15,7 +24,7 @@ create table if not exists hs_office_membership
partnerUuid uuid not null references hs_office_partner(uuid), partnerUuid uuid not null references hs_office_partner(uuid),
memberNumberSuffix char(2) not null check (memberNumberSuffix::text ~ '^[0-9][0-9]$'), memberNumberSuffix char(2) not null check (memberNumberSuffix::text ~ '^[0-9][0-9]$'),
validity daterange not null, validity daterange not null,
reasonForTermination HsOfficeReasonForTermination not null default 'NONE', status HsOfficeMembershipStatus not null default 'ACTIVE',
membershipFeeBillable boolean not null default true, membershipFeeBillable boolean not null default true,
UNIQUE(partnerUuid, memberNumberSuffix) UNIQUE(partnerUuid, memberNumberSuffix)

View File

@ -172,7 +172,7 @@ call generateRbacRestrictedView('hs_office_membership',
$updates$ $updates$
validity = new.validity, validity = new.validity,
membershipFeeBillable = new.membershipFeeBillable, membershipFeeBillable = new.membershipFeeBillable,
reasonForTermination = new.reasonForTermination status = new.status
$updates$); $updates$);
--// --//

View File

@ -28,8 +28,8 @@ begin
raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix; raise notice 'creating test Membership: M-% %', forPartnerNumber, newMemberNumberSuffix;
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
insert insert
into hs_office_membership (uuid, partneruuid, memberNumberSuffix, validity, reasonfortermination) into hs_office_membership (uuid, partneruuid, memberNumberSuffix, validity, status)
values (uuid_generate_v4(), relatedPartner.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'NONE'); values (uuid_generate_v4(), relatedPartner.uuid, newMemberNumberSuffix, daterange('20221001' , null, '[]'), 'ACTIVE');
end; $$; end; $$;
--// --//

View File

@ -23,8 +23,8 @@ import jakarta.persistence.PersistenceContext;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.CANCELLATION; import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus.ACTIVE;
import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination.NONE; import static net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus.CANCELLED;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;
import static net.hostsharing.test.JsonMatcher.lenientlyEquals; import static net.hostsharing.test.JsonMatcher.lenientlyEquals;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -84,7 +84,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "01", "memberNumberSuffix": "01",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
}, },
{ {
"partner": { "partnerNumber": 10002 }, "partner": { "partnerNumber": 10002 },
@ -92,7 +92,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "02", "memberNumberSuffix": "02",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
}, },
{ {
"partner": { "partnerNumber": 10003 }, "partner": { "partnerNumber": 10003 },
@ -100,7 +100,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "03", "memberNumberSuffix": "03",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
} }
] ]
""")); """));
@ -131,7 +131,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "01", "memberNumberSuffix": "01",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
} }
] ]
""")); """));
@ -159,7 +159,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "02", "memberNumberSuffix": "02",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
} }
] ]
""")); """));
@ -239,7 +239,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "01", "memberNumberSuffix": "01",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
} }
""")); // @formatter:on """)); // @formatter:on
} }
@ -283,7 +283,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
"memberNumberSuffix": "03", "memberNumberSuffix": "03",
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
"reasonForTermination": "NONE" "status": "ACTIVE"
} }
""")); // @formatter:on """)); // @formatter:on
} }
@ -306,7 +306,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body(""" .body("""
{ {
"validTo": "2023-12-31", "validTo": "2023-12-31",
"reasonForTermination": "CANCELLATION" "status": "CANCELLED"
} }
""") """)
.port(port) .port(port)
@ -320,7 +320,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix())) .body("memberNumberSuffix", is(givenMembership.getMemberNumberSuffix()))
.body("validFrom", is("2022-11-01")) .body("validFrom", is("2022-11-01"))
.body("validTo", is("2023-12-31")) .body("validTo", is("2023-12-31"))
.body("reasonForTermination", is("CANCELLATION")); .body("status", is("CANCELLED"));
// @formatter:on // @formatter:on
// finally, the Membership is actually updated // finally, the Membership is actually updated
@ -329,7 +329,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
assertThat(mandate.getPartner().toShortString()).isEqualTo("P-10001"); assertThat(mandate.getPartner().toShortString()).isEqualTo("P-10001");
assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix()); assertThat(mandate.getMemberNumberSuffix()).isEqualTo(givenMembership.getMemberNumberSuffix());
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-01)");
assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION); assertThat(mandate.getStatus()).isEqualTo(CANCELLED);
return true; return true;
}); });
} }
@ -351,7 +351,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.body(""" .body("""
{ {
"validTo": "2024-01-01", "validTo": "2024-01-01",
"reasonForTermination": "CANCELLATION" "status": "CANCELLED"
} }
""") """)
.port(port) .port(port)
@ -364,7 +364,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get() assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-02)"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2024-01-02)");
assertThat(mandate.getReasonForTermination()).isEqualTo(CANCELLATION); assertThat(mandate.getStatus()).isEqualTo(CANCELLED);
return true; return true;
}); });
} }
@ -441,7 +441,7 @@ class HsOfficeMembershipControllerAcceptanceTest extends ContextBasedTestWithCle
.partner(givenPartner) .partner(givenPartner)
.memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX) .memberNumberSuffix(TEMP_MEMBER_NUMBER_SUFFIX)
.validity(Range.closedInfinite(LocalDate.parse("2022-11-01"))) .validity(Range.closedInfinite(LocalDate.parse("2022-11-01")))
.reasonForTermination(NONE) .status(ACTIVE)
.membershipFeeBillable(true) .membershipFeeBillable(true)
.build(); .build();

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.membership;
import io.hypersistence.utils.hibernate.type.range.Range; import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeReasonForTerminationResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipStatusResource;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.test.PatchUnitTestBase; import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -79,11 +79,11 @@ class HsOfficeMembershipEntityPatcherUnitTest extends PatchUnitTestBase<
PATCHED_VALID_TO, PATCHED_VALID_TO,
HsOfficeMembershipEntity::setValidTo), HsOfficeMembershipEntity::setValidTo),
new SimpleProperty<>( new SimpleProperty<>(
"reasonForTermination", "status",
HsOfficeMembershipPatchResource::setReasonForTermination, HsOfficeMembershipPatchResource::setStatus,
HsOfficeReasonForTerminationResource.CANCELLATION, HsOfficeMembershipStatusResource.CANCELLED,
HsOfficeMembershipEntity::setReasonForTermination, HsOfficeMembershipEntity::setStatus,
HsOfficeReasonForTermination.CANCELLATION) HsOfficeMembershipStatus.CANCELLED)
.notNullable(), .notNullable(),
new JsonNullableProperty<>( new JsonNullableProperty<>(
"membershipFeeBillable", "membershipFeeBillable",

View File

@ -62,27 +62,27 @@ class HsOfficeMembershipEntityUnitTest {
} }
@Test @Test
void getValidtyIfNull() { void getEmptyValidtyIfNull() {
givenMembership.setValidity(null); givenMembership.setValidity(null);
final var result = givenMembership.getValidity(); final var result = givenMembership.getValidity();
assertThat(result).isEqualTo(Range.infinite(LocalDate.class)); assertThat(result.isEmpty()).isTrue();
} }
@Test @Test
void initializesReasonForTerminationInPrePersistIfNull() throws Exception { void initializesStatusInPrePersistIfNull() throws Exception {
final var givenUninitializedMembership = new HsOfficeMembershipEntity(); final var givenUninitializedMembership = new HsOfficeMembershipEntity();
assertThat(givenUninitializedMembership.getReasonForTermination()).as("precondition failed").isNull(); assertThat(givenUninitializedMembership.getStatus()).as("precondition failed").isNull();
invokePrePersist(givenUninitializedMembership); invokePrePersist(givenUninitializedMembership);
assertThat(givenUninitializedMembership.getReasonForTermination()).isEqualTo(HsOfficeReasonForTermination.NONE); assertThat(givenUninitializedMembership.getStatus()).isEqualTo(HsOfficeMembershipStatus.INVALID);
} }
@Test @Test
void doesNotOverwriteReasonForTerminationInPrePersistIfNotNull() throws Exception { void doesNotOverwriteStatusInPrePersistIfNotNull() throws Exception {
givenMembership.setReasonForTermination(HsOfficeReasonForTermination.CANCELLATION); givenMembership.setStatus(HsOfficeMembershipStatus.CANCELLED);
invokePrePersist(givenMembership); invokePrePersist(givenMembership);
assertThat(givenMembership.getReasonForTermination()).isEqualTo(HsOfficeReasonForTermination.CANCELLATION); assertThat(givenMembership.getStatus()).isEqualTo(HsOfficeMembershipStatus.CANCELLED);
} }
@Test @Test

View File

@ -161,9 +161,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// then // then
exactlyTheseMembershipsAreReturned( exactlyTheseMembershipsAreReturned(
result, result,
"Membership(M-1000101, P-10001, [2022-10-01,), NONE)", "Membership(M-1000101, P-10001, [2022-10-01,), ACTIVE)",
"Membership(M-1000202, P-10002, [2022-10-01,), NONE)", "Membership(M-1000202, P-10002, [2022-10-01,), ACTIVE)",
"Membership(M-1000303, P-10003, [2022-10-01,), NONE)"); "Membership(M-1000303, P-10003, [2022-10-01,), ACTIVE)");
} }
@Test @Test
@ -177,7 +177,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
// then // then
exactlyTheseMembershipsAreReturned(result, exactlyTheseMembershipsAreReturned(result,
"Membership(M-1000101, P-10001, [2022-10-01,), NONE)"); "Membership(M-1000101, P-10001, [2022-10-01,), ACTIVE)");
} }
@Test @Test
@ -192,7 +192,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
assertThat(result) assertThat(result)
.isNotNull() .isNotNull()
.extracting(Object::toString) .extracting(Object::toString)
.isEqualTo("Membership(M-1000202, P-10002, [2022-10-01,), NONE)"); .isEqualTo("Membership(M-1000202, P-10002, [2022-10-01,), ACTIVE)");
} }
} }
@ -213,7 +213,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
givenMembership.setValidity(Range.closedOpen( givenMembership.setValidity(Range.closedOpen(
givenMembership.getValidity().lower(), newValidityEnd)); givenMembership.getValidity().lower(), newValidityEnd));
givenMembership.setReasonForTermination(HsOfficeReasonForTermination.CANCELLATION); givenMembership.setStatus(HsOfficeMembershipStatus.CANCELLED);
return toCleanup(membershipRepo.save(givenMembership)); return toCleanup(membershipRepo.save(givenMembership));
}); });

View File

@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransact
import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionType; import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionType;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipStatus;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
@ -54,6 +54,7 @@ import java.util.stream.Collectors;
import static java.lang.Boolean.parseBoolean; import static java.lang.Boolean.parseBoolean;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -185,6 +186,7 @@ public class ImportOfficeData extends ContextBasedTest {
17=partner(P-10017: null null, null), 17=partner(P-10017: null null, null),
20=partner(P-10020: null null, null), 20=partner(P-10020: null null, null),
22=partner(P-11022: null null, null), 22=partner(P-11022: null null, null),
90=partner(P-19090: null null, null),
99=partner(P-19999: null null, null) 99=partner(P-19999: null null, null)
} }
"""); """);
@ -194,14 +196,15 @@ public class ImportOfficeData extends ContextBasedTest {
17=debitor(D-1001700: rel(anchor='null null, null', type='DEBITOR'), mih), 17=debitor(D-1001700: rel(anchor='null null, null', type='DEBITOR'), mih),
20=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz), 20=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz),
22=debitor(D-1102200: rel(anchor='null null, null', type='DEBITOR'), xxx), 22=debitor(D-1102200: rel(anchor='null null, null', type='DEBITOR'), xxx),
90=debitor(D-1909000: rel(anchor='null null, null', type='DEBITOR'), yyy),
99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz) 99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz)
} }
"""); """);
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{ {
17=Membership(M-1001700, P-10017, [2000-12-06,), NONE), 17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE),
20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), 20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
22=Membership(M-1102200, P-11022, [2021-04-01,), NONE) 22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE)
} }
"""); """);
} }
@ -228,6 +231,7 @@ public class ImportOfficeData extends ContextBasedTest {
17=partner(P-10017: NP Mellies, Michael, Herr Michael Mellies ), 17=partner(P-10017: NP Mellies, Michael, Herr Michael Mellies ),
20=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH), 20=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH),
22=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS), 22=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS),
90=partner(P-19090: NP Camus, Cecilia, Frau Cecilia Camus ),
99=partner(P-19999: null null, null) 99=partner(P-19999: null null, null)
} }
"""); """);
@ -240,7 +244,8 @@ public class ImportOfficeData extends ContextBasedTest {
1203=contact(label='Herr Philip Meyer-Contract , JM GmbH', emailAddresses='pm-partner@example.org'), 1203=contact(label='Herr Philip Meyer-Contract , JM GmbH', emailAddresses='pm-partner@example.org'),
1204=contact(label='Frau Tammy Meyer-VIP , JM GmbH', emailAddresses='tm-vip@example.org'), 1204=contact(label='Frau Tammy Meyer-VIP , JM GmbH', emailAddresses='tm-vip@example.org'),
1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com'), 1301=contact(label='Petra Schmidt , Test PS', emailAddresses='ps@example.com'),
1401=contact(label='Frau Frauke Fanninga ', emailAddresses='ff@example.org') 1401=contact(label='Frau Frauke Fanninga ', emailAddresses='ff@example.org'),
1501=contact(label='Frau Cecilia Camus ', emailAddresses='cc@example.org')
} }
"""); """);
assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace("""
@ -253,7 +258,8 @@ public class ImportOfficeData extends ContextBasedTest {
1203=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'), 1203=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'),
1204=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-VIP', givenName='Tammy'), 1204=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-VIP', givenName='Tammy'),
1301=person(personType='??', tradeName='Test PS', familyName='Schmidt', givenName='Petra'), 1301=person(personType='??', tradeName='Test PS', familyName='Schmidt', givenName='Petra'),
1401=person(personType='NP', tradeName='', familyName='Fanninga', givenName='Frauke') 1401=person(personType='NP', tradeName='', familyName='Fanninga', givenName='Frauke'),
1501=person(personType='NP', tradeName='', familyName='Camus', givenName='Cecilia')
} }
"""); """);
assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace("""
@ -261,14 +267,15 @@ public class ImportOfficeData extends ContextBasedTest {
17=debitor(D-1001700: rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael'), mih), 17=debitor(D-1001700: rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael'), mih),
20=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz), 20=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz),
22=debitor(D-1102200: rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS'), xxx), 22=debitor(D-1102200: rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS'), xxx),
90=debitor(D-1909000: rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia'), yyy),
99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz) 99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz)
} }
"""); """);
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{ {
17=Membership(M-1001700, P-10017, [2000-12-06,), NONE), 17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE),
20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), 20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
22=Membership(M-1102200, P-11022, [2021-04-01,), NONE) 22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE)
} }
"""); """);
assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace(""" assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace("""
@ -279,21 +286,25 @@ public class ImportOfficeData extends ContextBasedTest {
2000003=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), 2000003=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
2000004=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000004=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000005=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000005=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000006=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'), 2000006=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
2000007=rel(anchor='null null, null', type='DEBITOR'), 2000007=rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
2000008=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000008=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'),
2000009=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), 2000009=rel(anchor='null null, null', type='DEBITOR'),
2000010=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'), 2000010=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000011=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000011=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '),
2000012=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000012=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
2000013=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000013=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000014=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000014=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000015=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000015=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000016=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000016=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000017=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'), 2000017=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000018=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000018=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000019=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000019=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
2000020=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga ') 2000020=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000021=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000022=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
2000023=rel(anchor='NP Camus, Cecilia', type='OPERATIONS', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
2000024=rel(anchor='NP Camus, Cecilia', type='REPRESENTATIVE', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus ')
} }
"""); """);
} }
@ -383,7 +394,23 @@ public class ImportOfficeData extends ContextBasedTest {
33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, for transfer from 7), 33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, for transfer from 7),
34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, for cancellation D), 34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, for cancellation D),
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, for cancellation D), 34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, for cancellation D),
34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, for cancellation D) 34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, for cancellation D),
35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, for subscription E),
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, chargeback for subscription E)
}
""");
}
@Test
@Order(1099)
void verifyMemberships() {
assumeThatWeAreImportingControlledTestData();
assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace("""
{
17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE),
20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN),
22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE),
90=Membership(M-1909000, P-19090, empty, INVALID)
} }
"""); """);
} }
@ -742,10 +769,10 @@ public class ImportOfficeData extends ContextBasedTest {
rec.getLocalDate("member_since"), rec.getLocalDate("member_since"),
rec.getLocalDate("member_until"))) rec.getLocalDate("member_until")))
.membershipFeeBillable(rec.isEmpty("member_role")) .membershipFeeBillable(rec.isEmpty("member_role"))
.reasonForTermination( .status(
isBlank(rec.getString("member_until")) isBlank(rec.getString("member_until"))
? HsOfficeReasonForTermination.NONE ? HsOfficeMembershipStatus.ACTIVE
: HsOfficeReasonForTermination.UNKNOWN) : HsOfficeMembershipStatus.UNKNOWN)
.build(); .build();
memberships.put(rec.getInteger("bp_id"), membership); memberships.put(rec.getInteger("bp_id"), membership);
} }
@ -760,7 +787,9 @@ public class ImportOfficeData extends ContextBasedTest {
.map(this::trimAll) .map(this::trimAll)
.map(row -> new Record(columns, row)) .map(row -> new Record(columns, row))
.forEach(rec -> { .forEach(rec -> {
final var member = memberships.get(rec.getInteger("bp_id")); final var bpId = rec.getInteger("bp_id");
final var member = ofNullable(memberships.get(bpId))
.orElseGet(() -> createOnDemandMembership(bpId));
final var shareTransaction = HsOfficeCoopSharesTransactionEntity.builder() final var shareTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.membership(member) .membership(member)
@ -788,11 +817,14 @@ public class ImportOfficeData extends ContextBasedTest {
.map(this::trimAll) .map(this::trimAll)
.map(row -> new Record(columns, row)) .map(row -> new Record(columns, row))
.forEach(rec -> { .forEach(rec -> {
final var member = memberships.get(rec.getInteger("bp_id")); final var bpId = rec.getInteger("bp_id");
final var member = ofNullable(memberships.get(bpId))
.orElseGet(() -> createOnDemandMembership(bpId));
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() { final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
{ {
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.ADJUSTMENT);
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER); put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION); put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS); put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
@ -823,6 +855,17 @@ public class ImportOfficeData extends ContextBasedTest {
}); });
} }
private static HsOfficeMembershipEntity createOnDemandMembership(final Integer bpId) {
final var onDemandMembership = HsOfficeMembershipEntity.builder()
.memberNumberSuffix("00")
.membershipFeeBillable(false)
.partner(partners.get(bpId))
.status(HsOfficeMembershipStatus.INVALID)
.build();
memberships.put(bpId, onDemandMembership);
return onDemandMembership;
}
private void importSepaMandates(final String[] header, final List<String[]> records) { private void importSepaMandates(final String[] header, final List<String[]> records) {
final var columns = new Columns(header); final var columns = new Columns(header);

View File

@ -86,7 +86,7 @@ class HsOfficePersonEntityUnitTest {
final var actualDisplay = givenPersonEntity.toShortString(); final var actualDisplay = givenPersonEntity.toShortString();
assertThat(actualDisplay).isEqualTo("NP Frau some family name, some given name"); assertThat(actualDisplay).isEqualTo("NP some family name, some given name");
} }
@Test @Test

View File

@ -1,9 +1,11 @@
member_asset_id; bp_id; date; action; amount; comment member_asset_id; bp_id; date; action; amount; comment
30000; 17; 2000-12-06; PAYMENT; 1280.00; for subscription A 30000; 17; 2000-12-06; PAYMENT; 1280.00; for subscription A
31000; 20; 2000-12-06; PAYMENT; 128.00; for subscription B 31000; 20; 2000-12-06; PAYMENT; 128.00; for subscription B
32000; 17; 2005-01-10; PAYMENT; 2560.00; for subscription C 32000; 17; 2005-01-10; PAYMENT; 2560.00; for subscription C
33001; 17; 2005-01-10; HANDOVER; -512.00; for transfer to 10 33001; 17; 2005-01-10; HANDOVER; -512.00; for transfer to 10
33002; 20; 2005-01-10; ADOPTION; 512.00; for transfer from 7 33002; 20; 2005-01-10; ADOPTION; 512.00; for transfer from 7
34001; 20; 2016-12-31; CLEARING; -8.00; for cancellation D 34001; 20; 2016-12-31; CLEARING; -8.00; for cancellation D
34002; 20; 2016-12-31; PAYBACK; -100.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 34003; 20; 2016-12-31; LOSS; -20.00; for cancellation D
35001; 90; 2024-01-15; PAYMENT; 128.00; for subscription E
35002; 90; 2024-01-20; ADJUSTMENT;-128.00; chargeback for subscription E

1 member_asset_id bp_id date action amount comment
2 30000 17 2000-12-06 PAYMENT 1280.00 for subscription A
3 31000 20 2000-12-06 PAYMENT 128.00 for subscription B
4 32000 17 2005-01-10 PAYMENT 2560.00 for subscription C
5 33001 17 2005-01-10 HANDOVER -512.00 for transfer to 10
6 33002 20 2005-01-10 ADOPTION 512.00 for transfer from 7
7 34001 20 2016-12-31 CLEARING -8.00 for cancellation D
8 34002 20 2016-12-31 PAYBACK -100.00 for cancellation D
9 34003 20 2016-12-31 LOSS -20.00 for cancellation D
10 35001 90 2024-01-15 PAYMENT 128.00 for subscription E
11 35002 90 2024-01-20 ADJUSTMENT -128.00 chargeback for subscription E

View File

@ -2,4 +2,5 @@ bp_id;member_id;member_code;member_since;member_until;member_role;author_contrac
17;10017;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 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; 20;10020;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS;
22;11022;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; 22;11022;hsh00-xxx;2021-04-01;;;;;true;true;GROSS;
90;19090;hsh00-yyy;;;;;;true;true;GROSS;
99;19999;hsh00-zzz;;;;;;false;false;GROSS; 99;19999;hsh00-zzz;;;;;;false;false;GROSS;

1 bp_id member_id member_code member_since member_until member_role author_contract nondisc_contract free exempt_vat indicator_vat uid_vat
2 17 10017 hsh00-mih 2000-12-06 Aufsichtsrat 2006-10-15 2001-10-15 false false NET DE-VAT-007
3 20 10020 hsh00-xyz 2000-12-06 2015-12-31 false false GROSS
4 22 11022 hsh00-xxx 2021-04-01 true true GROSS
5 90 19090 hsh00-yyy true true GROSS
6 99 19999 hsh00-zzz false false GROSS

View File

@ -15,3 +15,6 @@ contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zip
# eine natürliche Person, die nur Subscriber ist # eine natürliche Person, die nur Subscriber ist
1401; 17; Frau; Frauke; Fanninga; ; ; ; Am Walde 1; 29456; Hitzacker; DE; ; ; ;; ff@example.org; subscriber:operations-announce 1401; 17; Frau; Frauke; Fanninga; ; ; ; Am Walde 1; 29456; Hitzacker; DE; ; ; ;; ff@example.org; subscriber:operations-announce
# eine natürliche Person als Partner
1501; 90; Frau; Cecilia; Camus; ; ; ; Rue d'Avignion 60; 45000; Orléans; FR; ; ; ;; cc@example.org; partner,contractual,billing,operation

1 contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zipcode;city; country; phone_private; phone_office; phone_mobile; fax; email; roles
15 1501; 90; Frau; Cecilia; Camus; ; ; ; Rue d'Avignion 60; 45000; Orléans; FR; ; ; ;; cc@example.org; partner,contractual,billing,operation
16
17
18
19
20