Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Hoennig
b00c4ce8a8 reactivate uncommented test and remove commented code 2024-01-20 15:17:43 +01:00
Michael Hoennig
fe4b0866ed improved aliases 2024-01-20 14:46:42 +01:00
Michael Hoennig
52d4b4d458 add refundBankAccount to DebitorEntityPatcher 2024-01-20 14:46:35 +01:00
Michael Hoennig
837e7fee97 in target diagram: introduce partner-person partner-role to Hostsharing eG 2024-01-20 13:09:41 +01:00
9 changed files with 137 additions and 74 deletions

View File

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

View File

@ -4,31 +4,69 @@
classDiagram classDiagram
direction TD direction TD
namespace Partner { namespace Hostsharing {
class person-HostsharingEG
class partner-MeierGmbH }
class personDetails-MeierGmbH
class contactData-MeierGmbH
class person-MeierGmbH
class person-FrankMeier
class contactData-FrankMeier
class role-MeierGmbH-FrankMeier
class person-SabineMeier namespace Partner {
class contactData-SabineMeier class partner-MeierGmbH
class role-MeierGmbH-SabineMeier 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 debitor-MeierGmbH
class contactData-MeierGmbH-Buha class contactData-MeierGmbH-Buha
class role-MeierGmbH-Buha class role-MeierGmbH-Buha
} }
class partner-MeierGmbH { namespace Operations {
+partnerNumber: 12345 class person-SabineMeier
+person class contactData-SabineMeier
class role-MeierGmbH-SabineMeier
} }
partner-MeierGmbH o-- person-MeierGmbH
namespace Enums {
class RoleType {
<<enumeration>>
UNKNOWN
REPRESENTATIVE
ACCOUNTING
OPERATIONS
}
class PersonType {
<<enumeration>>
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 { class person-MeierGmbH {
+personType: LEGAL +personType: LEGAL
@ -36,17 +74,15 @@ classDiagram
+familyName +familyName
+givenName +givenName
} }
person-MeierGmbH *-- personDetails-MeierGmbH : optiona person-MeierGmbH *-- personDetails-MeierGmbH
class personDetails-MeierGmbH { class personDetails-MeierGmbH {
+personContact
+registrationOffice: AG Hamburg +registrationOffice: AG Hamburg
+registrationNumber: ABC123434 +registrationNumber: ABC123434
+birthName +birthName
+birthPlace +birthPlace
+dateOfDeath +dateOfDeath
} }
personDetails-MeierGmbH o-- contactData-MeierGmbH
class contactData-MeierGmbH { class contactData-MeierGmbH {
+postalAddress: Hauptstraße 5, 22345 Hamburg +postalAddress: Hauptstraße 5, 22345 Hamburg
@ -54,6 +90,18 @@ classDiagram
+emailAddresses: office@meier-gmbh.de +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 { class debitor-MeierGmbH {
+Partner partner +Partner partner
+Numeric[2] debitorNumberSuffix: 00 +Numeric[2] debitorNumberSuffix: 00
@ -85,6 +133,8 @@ classDiagram
role-MeierGmbH-Buha o-- person-MeierGmbH : holder role-MeierGmbH-Buha o-- person-MeierGmbH : holder
role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha role-MeierGmbH-Buha o-- contactData-MeierGmbH-Buha
%% --- Representatives ---
class person-FrankMeier { class person-FrankMeier {
+ personType: NATURAL + personType: NATURAL
+ tradeName + tradeName
@ -108,6 +158,8 @@ classDiagram
role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder role-MeierGmbH-FrankMeier o-- person-FrankMeier : holder
role-MeierGmbH-FrankMeier o-- contactData-FrankMeier role-MeierGmbH-FrankMeier o-- contactData-FrankMeier
%% --- Operations ---
class person-SabineMeier { class person-SabineMeier {
+personType: NATURAL +personType: NATURAL
+tradeName +tradeName
@ -131,25 +183,4 @@ classDiagram
role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder role-MeierGmbH-SabineMeier o-- person-SabineMeier : holder
role-MeierGmbH-SabineMeier o-- contactData-SabineMeier role-MeierGmbH-SabineMeier o-- contactData-SabineMeier
namespace Enums {
class RoleType {
<<enumeration>>
UNKNOWN
REPRESENTATIVE
ACCOUNTING
OPERATIONS
}
class PersonType {
<<enumeration>>
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]
}
}
``` ```

View File

@ -1,5 +1,6 @@
package net.hostsharing.hsadminng.hs.office.debitor; 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.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
@ -35,6 +36,10 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
verifyNotNull(newValue, "defaultPrefix"); verifyNotNull(newValue, "defaultPrefix");
entity.setDefaultPrefix(newValue); entity.setDefaultPrefix(newValue);
}); });
OptionalFromJson.of(resource.getRefundBankAccountUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "refundBankAccount");
entity.setRefundBankAccount(em.getReference(HsOfficeBankAccountEntity.class, newValue));
});
} }
private void verifyNotNull(final Object newValue, final String propertyName) { private void verifyNotNull(final Object newValue, final String propertyName) {

View File

@ -17,7 +17,7 @@ create table hs_office_debitor
vatReverseCharge boolean not null, vatReverseCharge boolean not null,
refundBankAccountUuid uuid references hs_office_bankaccount(uuid), refundBankAccountUuid uuid references hs_office_bankaccount(uuid),
defaultPrefix char(3) not null 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)$' defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$'
) )
-- TODO.impl: SEPA-mandate -- TODO.impl: SEPA-mandate

View File

@ -33,7 +33,6 @@ begin
select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact; 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 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 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact; raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;

View File

@ -8,14 +8,11 @@
/* /*
Creates a single membership test record. Creates a single membership test record.
*/ */
-- create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber integer )
create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric ) create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric )
language plpgsql as $$ language plpgsql as $$
declare declare
currentTask varchar; currentTask varchar;
idName varchar; idName varchar;
-- forDebitorNumberPrefix integer;
-- forDebitorNumberSuffix integer;
relatedPartner hs_office_partner; relatedPartner hs_office_partner;
relatedDebitor hs_office_debitor; relatedDebitor hs_office_debitor;
newMemberNumber numeric; newMemberNumber numeric;
@ -28,9 +25,6 @@ begin
select partner.* from hs_office_partner partner select partner.* from hs_office_partner partner
join hs_office_person person on person.uuid = partner.personUuid join hs_office_person person on person.uuid = partner.personUuid
where person.tradeName = forPartnerTradeName into relatedPartner; 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 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(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber;
@ -39,7 +33,6 @@ begin
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert insert
into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination) 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'); values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE');
end; $$; end; $$;
--// --//
@ -55,10 +48,5 @@ do language plpgsql $$
call createHsOfficeMembershipTestData('Second e.K.', 12); call createHsOfficeMembershipTestData('Second e.K.', 12);
call createHsOfficeMembershipTestData('Third OHG', 13); call createHsOfficeMembershipTestData('Third OHG', 13);
end; end;
-- begin
-- call createHsOfficeMembershipTestData('First GmbH', 1000100);
-- call createHsOfficeMembershipTestData('Second e.K.', 1000200);
-- call createHsOfficeMembershipTestData('Third OHG', 1000300);
-- end;
$$; $$;
--// --//

View File

@ -1,5 +1,6 @@
package net.hostsharing.hsadminng.hs.office.debitor; 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.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; 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 INITIAL_VAT_REVERSE_CHARGE = true;
private static final boolean PATCHED_VAT_REVERSE_CHARGE = false; 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() private final HsOfficePartnerEntity givenInitialPartner = HsOfficePartnerEntity.builder()
.uuid(INITIAL_PARTNER_UUID) .uuid(INITIAL_PARTNER_UUID)
.build(); .build();
@ -49,6 +53,10 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder() private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder()
.uuid(INITIAL_CONTACT_UUID) .uuid(INITIAL_CONTACT_UUID)
.build(); .build();
private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder()
.uuid(INITIAL_REFUND_BANK_ACCOUNT_UUID)
.build();
@Mock @Mock
private EntityManager em; private EntityManager em;
@ -56,8 +64,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
void initMocks() { void initMocks() {
lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation ->
HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build());
lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation ->
HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build());
} }
@Override @Override
@ -72,6 +80,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
entity.setVatBusiness(true); entity.setVatBusiness(true);
entity.setVatReverseCharge(INITIAL_VAT_REVERSE_CHARGE); entity.setVatReverseCharge(INITIAL_VAT_REVERSE_CHARGE);
entity.setDefaultPrefix("abc"); entity.setDefaultPrefix("abc");
entity.setRefundBankAccount(givenInitialBankAccount);
return entity; return entity;
} }
@ -120,7 +129,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
new SimpleProperty<>( new SimpleProperty<>(
"personType", "personType",
HsOfficeDebitorPatchResource::setVatBusiness, HsOfficeDebitorPatchResource::setVatBusiness,
PATCHED_BILLABLE, PATCHED_VAT_BUSINESS,
HsOfficeDebitorEntity::setVatBusiness) HsOfficeDebitorEntity::setVatBusiness)
.notNullable(), .notNullable(),
new SimpleProperty<>( new SimpleProperty<>(
@ -134,6 +143,13 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficeDebitorPatchResource::setDefaultPrefix, HsOfficeDebitorPatchResource::setDefaultPrefix,
PATCHED_DEFAULT_PREFIX, PATCHED_DEFAULT_PREFIX,
HsOfficeDebitorEntity::setDefaultPrefix) HsOfficeDebitorEntity::setDefaultPrefix)
.notNullable(),
new JsonNullableProperty<>(
"refundBankAccount",
HsOfficeDebitorPatchResource::setRefundBankAccountUuid,
PATCHED_REFUND_BANK_ACCOUNT_UUID,
HsOfficeDebitorEntity::setRefundBankAccount,
newBankAccount(PATCHED_REFUND_BANK_ACCOUNT_UUID))
.notNullable() .notNullable()
); );
} }
@ -143,4 +159,10 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
newContact.setUuid(uuid); newContact.setUuid(uuid);
return newContact; return newContact;
} }
private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) {
final var newBankAccount = new HsOfficeBankAccountEntity();
newBankAccount.setUuid(uuid);
return newBankAccount;
}
} }

View File

@ -20,14 +20,13 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.orm.jpa.JpaSystemException; import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.grantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf; import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.roleNamesOf;
@ -65,8 +64,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
@MockBean @MockBean
HttpServletRequest request; HttpServletRequest request;
Set<HsOfficeDebitorEntity> tempDebitors = new HashSet<>();
@Nested @Nested
class CreateDebitor { class CreateDebitor {
@ -97,6 +94,34 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
assertThat(debitorRepo.count()).isEqualTo(count + 1); 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 @Test
public void createsAndGrantsRoles() { public void createsAndGrantsRoles() {
// given // given
@ -564,14 +589,6 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
}).assertSuccessful().returnedValue(); }).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<HsOfficeDebitorEntity> actualResult, final String... debitorNames) { void exactlyTheseDebitorsAreReturned(final List<HsOfficeDebitorEntity> actualResult, final String... debitorNames) {
assertThat(actualResult) assertThat(actualResult)
.extracting(HsOfficeDebitorEntity::toString) .extracting(HsOfficeDebitorEntity::toString)

View File

@ -1,5 +1,6 @@
package net.hostsharing.test; package net.hostsharing.test;
import net.hostsharing.hsadminng.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
import org.junit.jupiter.api.Named; import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -232,7 +233,7 @@ public abstract class PatchUnitTestBase<R, E> {
} }
} }
protected static class JsonNullableProperty<R, RV, E, EV> extends Property<R, RV, E, EV> { protected static class JsonNullableProperty<R, RV, E extends HasUuid, EV> extends Property<R, RV, E, EV> {
private final BiConsumer<R, JsonNullable<RV>> resourceSetter; private final BiConsumer<R, JsonNullable<RV>> resourceSetter;
public final RV givenPatchValue; public final RV givenPatchValue;