introduce more lazy-loading references

This commit is contained in:
Michael Hoennig 2024-07-23 17:08:06 +02:00
parent b50bcbbca0
commit b79a8ca22d
33 changed files with 158 additions and 181 deletions

View File

@ -245,3 +245,17 @@ The HashJoin strategy could be great if the hash-map could be kept for multiple
### LAZY loading for Relation.anchorPerson/.holderPerson/
The slowest query now was fetching Relations joined with Contact, Anchor-Person and Holder-Person, for all tables using the restricted (RBAC) views (_rv).
We changed these mappings from `EAGER` (default) to `LAZY` to `@ManyToOne(fetch = FetchType.LAZY)` and got this result:
| query | calls | total (min) | mean (ms) |
|-------|-------|-------------|-----------|
| select hore1_0.uuid,hore1_0.anchoruuid,hore1_0.contactuuid,hore1_0.holderuuid,hore1_0.mark,hore1_0.type,hore1_0.version from hs_office_relation_rv hore1_0 where hore1_0.uuid=$1 | 517 | 5 | 565 |
| select hope1_0.uuid,hope1_0.familyname,hope1_0.givenname,hope1_0.persontype,hope1_0.salutation,hope1_0.title,hope1_0.tradename,hope1_0.version from hs_office_person_rv hope1_0 where hope1_0.uuid=$1 | 1015 | 4 | 240 |
| select hoce1_0.uuid,hoce1_0.caption,hoce1_0.emailaddresses,hoce1_0.phonenumbers,hoce1_0.postaladdress,hoce1_0.version from hs_office_contact_rv hoce1_0 where hoce1_0.uuid=$1 | 497 | 2 | 235
select * from isGranted(array[granteeId], grantedId) | 44613 | 1 | 1 |
Now, finally, the total runtime of the import was down to 12 minutes.

View File

@ -70,7 +70,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsBookingItemEntity implements Stringifyable, RbacObject, PropertiesProvider {
public class HsBookingItemEntity implements Stringifyable, RbacObject<HsBookingItemEntity>, PropertiesProvider {
private static Stringify<HsBookingItemEntity> stringify = stringify(HsBookingItemEntity.class)
.withProp(HsBookingItemEntity::getProject)

View File

@ -34,7 +34,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsBookingProjectEntity implements Stringifyable, RbacObject {
public class HsBookingProjectEntity implements Stringifyable, RbacObject<HsBookingProjectEntity> {
private static Stringify<HsBookingProjectEntity> stringify = stringify(HsBookingProjectEntity.class)
.withProp(HsBookingProjectEntity::getDebitor)

View File

@ -70,7 +70,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class HsHostingAssetEntity implements Stringifyable, RbacObject, PropertiesProvider {
public class HsHostingAssetEntity implements Stringifyable, RbacObject<HsHostingAssetEntity>, PropertiesProvider {
private static Stringify<HsHostingAssetEntity> stringify = stringify(HsHostingAssetEntity.class)
.withProp(HsHostingAssetEntity::getType)

View File

@ -27,7 +27,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("BankAccount")
public class HsOfficeBankAccountEntity implements RbacObject, Stringifyable {
public class HsOfficeBankAccountEntity implements RbacObject<HsOfficeBankAccountEntity>, Stringifyable {
private static Stringify<HsOfficeBankAccountEntity> toString = stringify(HsOfficeBankAccountEntity.class, "bankAccount")
.withIdProp(HsOfficeBankAccountEntity::getIban)

View File

@ -35,7 +35,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("Contact")
public class HsOfficeContactEntity implements Stringifyable, RbacObject {
public class HsOfficeContactEntity implements Stringifyable, RbacObject<HsOfficeContactEntity> {
private static Stringify<HsOfficeContactEntity> toString = stringify(HsOfficeContactEntity.class, "contact")
.withProp(Fields.caption, HsOfficeContactEntity::getCaption)

View File

@ -41,7 +41,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopAssetsTransaction")
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacObject {
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, RbacObject<HsOfficeCoopAssetsTransactionEntity> {
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withIdProp(HsOfficeCoopAssetsTransactionEntity::getTaggedMemberNumber)

View File

@ -39,7 +39,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject {
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, RbacObject<HsOfficeCoopSharesTransactionEntity> {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withIdProp(HsOfficeCoopSharesTransactionEntity::getMemberNumberTagged)

View File

@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeDebitors
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorInsertResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorResource;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRepository;
import net.hostsharing.hsadminng.mapper.Mapper;
@ -37,6 +38,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Autowired
private HsOfficeDebitorRepository debitorRepo;
@Autowired
private HsOfficePartnerRepository partnerRepo;
@Autowired
private HsOfficeRelationRepository relRepo;
@ -91,6 +95,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
() -> { throw new EntityNotFoundException("ERROR: [400] debitorRelUuid not found: " + body.getDebitorRelUuid());});
}
final var relatedPartner = partnerRepo.findPartnerByPartnerPersonUuid(entityToSave.getDebitorRel().getHolder().getUuid());
entityToSave.setPartnerNumber(relatedPartner.getPartnerNumber());
final var savedEntity = debitorRepo.save(entityToSave);
em.flush();
em.refresh(savedEntity);

View File

@ -7,13 +7,13 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationEntity;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.Hibernate;
import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.Column;
@ -54,7 +54,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Debitor")
public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
public class HsOfficeDebitorEntity implements RbacObject<HsOfficeDebitorEntity>, Stringifyable {
public static final String DEBITOR_NUMBER_TAG = "D-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@ -107,6 +107,16 @@ public class HsOfficeDebitorEntity implements RbacObject, Stringifyable {
@Column(name = "defaultprefix", columnDefinition = "char(3) not null")
private String defaultPrefix;
@Override
public HsOfficeDebitorEntity load() {
RbacObject.super.load();
debitorRel.load();
if (refundBankAccount != null) {
refundBankAccount.load();
}
return this;
}
private String getDebitorNumberString() {
return ofNullable(partnerNumber)
.filter(partner -> debitorNumberSuffix != null)

View File

@ -61,7 +61,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Membership")
public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
public class HsOfficeMembershipEntity implements RbacObject<HsOfficeMembershipEntity>, Stringifyable {
public static final String MEMBER_NUMBER_TAG = "M-";
public static final String TWO_DECIMAL_DIGITS = "^([0-9]{2})$";
@ -99,6 +99,13 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable {
@Enumerated(EnumType.STRING)
private HsOfficeMembershipStatus status;
@Override
public HsOfficeMembershipEntity load() {
RbacObject.super.load();
partner.load();
return this;
}
public void setValidFrom(final LocalDate validFrom) {
setValidity(toPostgresDateRange(validFrom, getValidTo()));
}

View File

@ -26,7 +26,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("PartnerDetails")
public class HsOfficePartnerDetailsEntity implements RbacObject, Stringifyable {
public class HsOfficePartnerDetailsEntity implements RbacObject<HsOfficePartnerDetailsEntity>, Stringifyable {
private static Stringify<HsOfficePartnerDetailsEntity> stringify = stringify(
HsOfficePartnerDetailsEntity.class,

View File

@ -40,7 +40,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("Partner")
public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
public class HsOfficePartnerEntity implements Stringifyable, RbacObject<HsOfficePartnerEntity> {
public static final String PARTNER_NUMBER_TAG = "P-";
@ -66,11 +66,11 @@ public class HsOfficePartnerEntity implements Stringifyable, RbacObject {
@Column(name = "partnernumber", columnDefinition = "numeric(5) not null")
private Integer partnerNumber;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false)
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = false, fetch = FetchType.EAGER)
@JoinColumn(name = "partnerreluuid", nullable = false)
private HsOfficeRelationEntity partnerRel;
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true)
@ManyToOne(cascade = { PERSIST, MERGE, REFRESH, DETACH }, optional = true, fetch = FetchType.EAGER)
@JoinColumn(name = "detailsuuid")
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details;

View File

@ -31,13 +31,11 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
@Query("""
SELECT DISTINCT partner
FROM HsOfficePartnerEntity partner
JOIN HsOfficeRelationEntity dRel
ON dRel.uuid = :debitorUuid AND dRel.type = 'DEBITOR'
JOIN HsOfficeRelationEntity pRel
ON pRel.uuid = partner.partnerRel.uuid AND pRel.type = 'PARTNER'
WHERE pRel.holder.uuid = dRel.anchor.uuid
WHERE pRel.holder.uuid = :partnerPersonUuid
""")
HsOfficePartnerEntity findPartnerByDebitorUuid(UUID debitorUuid);
HsOfficePartnerEntity findPartnerByPartnerPersonUuid(UUID partnerPersonUuid);
HsOfficePartnerEntity save(final HsOfficePartnerEntity entity);

View File

@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@AllArgsConstructor
@FieldNameConstants
@DisplayName("Person")
public class HsOfficePersonEntity implements RbacObject, Stringifyable {
public class HsOfficePersonEntity implements RbacObject<HsOfficePersonEntity>, Stringifyable {
private static Stringify<HsOfficePersonEntity> toString = stringify(HsOfficePersonEntity.class, "person")
.withProp(Fields.personType, HsOfficePersonEntity::getPersonType)

View File

@ -75,6 +75,15 @@ public class HsOfficeRelationEntity implements RbacObject, Stringifyable {
@Column(name = "mark")
private String mark;
@Override
public RbacObject<?> load() {
RbacObject.super.load();
anchor.load();
holder.load();
contact.load();
return this;
}
@Override
public String toString() {
return toString.apply(this);

View File

@ -40,7 +40,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("SEPA-Mandate")
public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject {
public class HsOfficeSepaMandateEntity implements Stringifyable, RbacObject<HsOfficeSepaMandateEntity> {
private static Stringify<HsOfficeSepaMandateEntity> stringify = stringify(HsOfficeSepaMandateEntity.class)
.withProp(e -> e.getBankAccount().getIban())

View File

@ -1,10 +1,18 @@
package net.hostsharing.hsadminng.rbac.rbacobject;
import org.hibernate.Hibernate;
import java.util.UUID;
public interface RbacObject {
public interface RbacObject<T extends RbacObject<?>> {
UUID getUuid();
int getVersion();
default T load() {
Hibernate.initialize(this);
//noinspection unchecked
return (T) this;
};
}

View File

@ -24,7 +24,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class TestCustomerEntity implements RbacObject {
public class TestCustomerEntity implements RbacObject<TestCustomerEntity> {
@Id
@GeneratedValue

View File

@ -27,7 +27,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class TestDomainEntity implements RbacObject {
public class TestDomainEntity implements RbacObject<TestDomainEntity> {
@Id
@GeneratedValue

View File

@ -27,7 +27,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class TestPackageEntity implements RbacObject {
public class TestPackageEntity implements RbacObject<TestPackageEntity> {
@Id
@GeneratedValue

View File

@ -287,7 +287,10 @@ class HsOfficeBankAccountControllerAcceptanceTest extends ContextBasedTestWithCl
.statusCode(204); // @formatter:on
// then the given bankaccount is still there
assertThat(bankAccountRepo.findByUuid(givenBankAccount.getUuid())).isEmpty();
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
assertThat(bankAccountRepo.findByUuid(givenBankAccount.getUuid())).isEmpty();
}).assertSuccessful();
}
@Test

View File

@ -309,7 +309,10 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
.statusCode(204); // @formatter:on
// then the given contact is gone
assertThat(contactRepo.findByUuid(givenContact.getUuid())).isEmpty();
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
assertThat(contactRepo.findByUuid(givenContact.getUuid())).isEmpty();
}).assertSuccessful();
}
@Test
@ -326,7 +329,10 @@ class HsOfficeContactControllerAcceptanceTest extends ContextBasedTestWithCleanu
.statusCode(204); // @formatter:on
// then the given contact is still there
assertThat(contactRepo.findByUuid(givenContact.getUuid())).isEmpty();
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", null);
assertThat(contactRepo.findByUuid(givenContact.getUuid())).isEmpty();
}).assertSuccessful();
}
@Test

View File

@ -119,7 +119,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
private void assertThatCoopAssetsTransactionIsPersisted(final HsOfficeCoopAssetsTransactionEntity saved) {
final var found = coopAssetsTransactionRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
assertThat(found).isNotEmpty().get().extracting(HsOfficeCoopAssetsTransactionEntity::toString).isEqualTo(saved.toString());
}
}

View File

@ -118,7 +118,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
private void assertThatCoopSharesTransactionIsPersisted(final HsOfficeCoopSharesTransactionEntity saved) {
final var found = coopSharesTransactionRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved);
assertThat(found).isNotEmpty().get().extracting(HsOfficeCoopSharesTransactionEntity::toString).isEqualTo(saved.toString());
}
}

View File

@ -113,38 +113,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
},
"debitorNumber": 1000111,
"debitorNumberSuffix": 11,
"partner": {
"partnerNumber": 10001,
"partnerRel": {
"anchor": {
"personType": "LEGAL_PERSON",
"tradeName": "Hostsharing eG",
"givenName": null,
"familyName": null
},
"holder": {
"personType": "LEGAL_PERSON",
"tradeName": "First GmbH",
"givenName": null,
"familyName": null
},
"type": "PARTNER",
"mark": null,
"contact": {
"caption": "first contact",
"emailAddresses": { "main": "contact-admin@firstcontact.example.com" },
"phoneNumbers": { "phone_office": "+49 123 1234567" }
}
},
"details": {
"registrationOffice": "Hamburg",
"registrationNumber": "RegNo123456789",
"birthName": null,
"birthPlace": null,
"birthday": null,
"dateOfDeath": null
}
},
"billable": true,
"vatId": null,
"vatCountryCode": null,
@ -168,21 +136,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
},
"debitorNumber": 1000212,
"debitorNumberSuffix": 12,
"partner": {
"partnerNumber": 10002,
"partnerRel": {
"anchor": {"tradeName": "Hostsharing eG"},
"holder": {"tradeName": "Second e.K."},
"type": "PARTNER",
"contact": {
"emailAddresses": { "main": "contact-admin@secondcontact.example.com" }
}
},
"details": {
"registrationOffice": "Hamburg",
"registrationNumber": "RegNo123456789"
}
},
"billable": true,
"vatId": null,
"vatCountryCode": null,
@ -202,21 +155,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
},
"debitorNumber": 1000313,
"debitorNumberSuffix": 13,
"partner": {
"partnerNumber": 10003,
"partnerRel": {
"anchor": {"tradeName": "Hostsharing eG"},
"holder": {"tradeName": "Third OHG"},
"type": "PARTNER",
"contact": {
"emailAddresses": { "main": "contact-admin@thirdcontact.example.com" }
}
},
"details": {
"registrationOffice": "Hamburg",
"registrationNumber": "RegNo123456789"
}
},
"billable": true,
"vatId": null,
"vatCountryCode": null,
@ -357,7 +295,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.contentType(ContentType.JSON)
.body("uuid", isUuidValid())
.body("debitorRel.contact.caption", is(givenContact.getCaption()))
.body("partner.partnerRel.holder.tradeName", is(givenPartner.getPartnerRel().getHolder().getTradeName()))
.body("vatId", equalTo(null))
.body("vatCountryCode", equalTo(null))
.body("vatBusiness", equalTo(false))
@ -471,25 +408,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
},
"debitorNumber": 1000111,
"debitorNumberSuffix": 11,
"partner": {
"partnerNumber": 10001,
"partnerRel": {
"anchor": { "personType": "LEGAL_PERSON", "tradeName": "Hostsharing eG"},
"holder": { "personType": "LEGAL_PERSON", "tradeName": "First GmbH"},
"type": "PARTNER",
"mark": null,
"contact": {
"caption": "first contact",
"postalAddress": "Vorname Nachname\\nStraße Hnr\\nPLZ Stadt",
"emailAddresses": { "main": "contact-admin@firstcontact.example.com" },
"phoneNumbers": { "phone_office": "+49 123 1234567" }
}
},
"details": {
"registrationOffice": "Hamburg",
"registrationNumber": "RegNo123456789"
}
},
"billable": true,
"vatBusiness": true,
"vatReverseCharge": false,
@ -583,24 +501,6 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
},
"debitorNumber": 10004${debitorNumberSuffix},
"debitorNumberSuffix": ${debitorNumberSuffix},
"partner": {
"partnerNumber": 10004,
"partnerRel": {
"anchor": { "tradeName": "Hostsharing eG" },
"holder": { "tradeName": "Fourth eG" },
"type": "PARTNER",
"mark": null,
"contact": { "caption": "fourth contact" }
},
"details": {
"registrationOffice": "Hamburg",
"registrationNumber": "RegNo123456789",
"birthName": null,
"birthPlace": null,
"birthday": null,
"dateOfDeath": null
}
},
"billable": true,
"vatId": "VAT222222",
"vatCountryCode": "AA",
@ -609,22 +509,24 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
"defaultPrefix": "for"
}
"""
.replace("${debitorNumberSuffix}", givenDebitor.getDebitorNumberSuffix().toString()))
.replace("${debitorNumberSuffix}", givenDebitor.getDebitorNumberSuffix()))
);
// @formatter:on
// finally, the debitor is actually updated
context.define("superuser-alex@hostsharing.net");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
.matches(debitor -> {
assertThat(debitor.getDebitorRel().getHolder().getTradeName())
.isEqualTo(givenDebitor.getDebitorRel().getHolder().getTradeName());
assertThat(debitor.getDebitorRel().getContact().getCaption()).isEqualTo("fourth contact");
assertThat(debitor.getVatId()).isEqualTo("VAT222222");
assertThat(debitor.getVatCountryCode()).isEqualTo("AA");
assertThat(debitor.isVatBusiness()).isEqualTo(true);
return true;
});
jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
.matches(debitor -> {
assertThat(debitor.getDebitorRel().getHolder().getTradeName())
.isEqualTo(givenDebitor.getDebitorRel().getHolder().getTradeName());
assertThat(debitor.getDebitorRel().getContact().getCaption()).isEqualTo("fourth contact");
assertThat(debitor.getVatId()).isEqualTo("VAT222222");
assertThat(debitor.getVatCountryCode()).isEqualTo("AA");
assertThat(debitor.isVatBusiness()).isEqualTo(true);
return true;
});
}).assertSuccessful();
}
@Test
@ -721,6 +623,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
final var givenContact = contactRepo.findContactByOptionalCaptionLike("fourth contact").get(0);
final var newDebitor = HsOfficeDebitorEntity.builder()
.partnerNumber(givenPartner.getPartnerNumber())
.debitorNumberSuffix(nextDebitorSuffix())
.billable(true)
.debitorRel(

View File

@ -89,6 +89,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when
final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder()
.partnerNumber(10001)
.debitorNumberSuffix("21")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
@ -99,7 +100,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
.defaultPrefix("abc")
.billable(false)
.build();
return toCleanup(debitorRepo.save(newDebitor));
final HsOfficeDebitorEntity entity = debitorRepo.save(newDebitor);
return toCleanup(entity.load());
});
// then
@ -156,6 +158,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitorPerson = one(personRepo.findPersonByOptionalNameLike("Fourth eG"));
final var givenContact = one(contactRepo.findContactByOptionalCaptionLike("fourth contact"));
final var newDebitor = HsOfficeDebitorEntity.builder()
.partnerNumber(10001)
.debitorNumberSuffix("22")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
@ -317,7 +320,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN", true);
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN");
final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
final var givenNewBillingPerson = one(personRepo.findPersonByOptionalNameLike("Firby"));
final var givenNewContact = one(contactRepo.findContactByOptionalCaptionLike("sixth contact"));
@ -339,14 +342,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
givenDebitor.setVatId(givenNewVatId);
givenDebitor.setVatCountryCode(givenNewVatCountryCode);
givenDebitor.setVatBusiness(givenNewVatBusiness);
return toCleanup(debitorRepo.save(givenDebitor));
final HsOfficeDebitorEntity entity = debitorRepo.save(givenDebitor);
return toCleanup(entity.load());
});
// then
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"global#global:ADMIN", true);
"global#global:ADMIN");
// ... partner role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
@ -354,7 +358,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan:AGENT", true);
"hs_office_relation#FirstGmbH-with-DEBITOR-FirbySusan:AGENT");
// ... contact role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
@ -362,7 +366,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"hs_office_contact#fifthcontact:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_contact#sixthcontact:ADMIN", false);
"hs_office_contact#sixthcontact:ADMIN");
// ... bank-account role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole(
@ -370,7 +374,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"hs_office_bankaccount#DE02200505501015871393:ADMIN");
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#DE02120300000000202051:ADMIN", true);
"hs_office_bankaccount#DE02120300000000202051:ADMIN");
}
@Test
@ -380,27 +384,27 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
"hs_office_relation#FourtheG-with-DEBITOR-FourtheG:ADMIN");
assertThatDebitorActuallyInDatabase(givenDebitor);
final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
givenDebitor.setRefundBankAccount(givenNewBankAccount);
return toCleanup(debitorRepo.save(givenDebitor));
return toCleanup(debitorRepo.save(givenDebitor).load());
});
// then
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"global#global:ADMIN", true);
"global#global:ADMIN");
// ... bank-account role was assigned:
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"hs_office_bankaccount#DE02120300000000202051:ADMIN", true);
"hs_office_bankaccount#DE02120300000000202051:ADMIN");
}
@Test
@ -410,21 +414,21 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fih");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT");
assertThatDebitorActuallyInDatabase(givenDebitor);
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
givenDebitor.setRefundBankAccount(null);
return toCleanup(debitorRepo.save(givenDebitor));
return toCleanup(debitorRepo.save(givenDebitor).load());
});
// then
result.assertSuccessful();
assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(),
"global#global:ADMIN", true);
"global#global:ADMIN");
// ... bank-account role was removed from previous bank-account admin:
assertThatDebitorIsNotVisibleForUserWithRole(
@ -439,8 +443,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT", true);
assertThatDebitorActuallyInDatabase(givenDebitor, true);
"hs_office_relation#HostsharingeG-with-PARTNER-FourtheG:AGENT");
assertThatDebitorActuallyInDatabase(givenDebitor);
// when
final var result = jpaAttempt.transacted(() -> {
@ -459,16 +463,17 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given
context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth", "nin");
assertThatDebitorActuallyInDatabase(givenDebitor, true);
assertThatDebitorActuallyInDatabase(givenDebitor);
assertThatDebitorIsVisibleForUserWithRole(
givenDebitor,
"hs_office_contact#ninthcontact:ADMIN", false);
"hs_office_contact#ninthcontact:ADMIN");
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_contact#ninthcontact:ADMIN");
givenDebitor.setVatId("NEW-VAT-ID");
return toCleanup(debitorRepo.save(givenDebitor));
final HsOfficeDebitorEntity entity = debitorRepo.save(givenDebitor);
return toCleanup(entity.load());
});
// then
@ -478,7 +483,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
"Unable to find net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity with id ");
}
private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved, final boolean withPartner) {
private void assertThatDebitorActuallyInDatabase(final HsOfficeDebitorEntity saved) {
final var found = debitorRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty();
found.ifPresent(foundEntity -> {
@ -492,11 +497,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
private void assertThatDebitorIsVisibleForUserWithRole(
final HsOfficeDebitorEntity entity,
final String assumedRoles,
final boolean withPartner) {
final String assumedRoles) {
jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", assumedRoles);
assertThatDebitorActuallyInDatabase(entity, withPartner);
assertThatDebitorActuallyInDatabase(entity);
}).assertSuccessful();
}
@ -605,11 +609,13 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
final String defaultPrefix) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike(partnerName));
final var givenPartner = one(partnerRepo.findPartnerByOptionalNameLike(partnerName));
final var givenPartnerPerson = givenPartner.getPartnerRel().getHolder();
final var givenContact = one(contactRepo.findContactByOptionalCaptionLike(contactCaption));
final var givenBankAccount =
bankAccountHolder != null ? one(bankAccountRepo.findByOptionalHolderLike(bankAccountHolder)) : null;
final var newDebitor = HsOfficeDebitorEntity.builder()
.partnerNumber(givenPartner.getPartnerNumber())
.debitorNumberSuffix("20")
.debitorRel(HsOfficeRelationEntity.builder()
.type(HsOfficeRelationType.DEBITOR)
@ -622,7 +628,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
.billable(true)
.build();
return toCleanup(debitorRepo.save(newDebitor));
final HsOfficeDebitorEntity entity = debitorRepo.save(newDebitor);
return toCleanup(entity.load());
}).assertSuccessful().returnedValue();
}

View File

@ -4,6 +4,7 @@ import io.hypersistence.utils.hibernate.type.range.Range;
import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject;
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
@ -75,7 +76,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
.validity(Range.closedInfinite(LocalDate.parse("2020-01-01")))
.membershipFeeBillable(true)
.build();
return toCleanup(membershipRepo.save(newMembership));
return toCleanup(membershipRepo.save(newMembership).load());
});
// then
@ -202,9 +203,12 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
@Test
public void globalAdmin_canUpdateValidityOfArbitraryMembership() {
// given
context("superuser-alex@hostsharing.net");
final var givenMembership = givenSomeTemporaryMembership("First", "11");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var givenMembership = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var tempMembership = givenSomeTemporaryMembership("First", "11");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(tempMembership);
return tempMembership;
}).assertSuccessful().returnedValue();
final var newValidityEnd = LocalDate.now();
// when
@ -214,13 +218,12 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
givenMembership.setValidity(Range.closedOpen(
givenMembership.getValidity().lower(), newValidityEnd));
givenMembership.setStatus(HsOfficeMembershipStatus.CANCELLED);
return toCleanup(membershipRepo.save(givenMembership));
final HsOfficeMembershipEntity entity = membershipRepo.save(givenMembership);
return toCleanup(entity.load());
});
// then
result.assertSuccessful();
membershipRepo.deleteByUuid(givenMembership.getUuid());
}
@Test
@ -363,7 +366,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTestWithCl
.membershipFeeBillable(true)
.build();
return toCleanup(membershipRepo.save(newMembership));
final HsOfficeMembershipEntity entity = membershipRepo.save(newMembership);
return toCleanup(entity.load());
}).assertSuccessful().returnedValue();
}

View File

@ -548,7 +548,7 @@ class HsOfficePartnerControllerAcceptanceTest extends ContextBasedTestWithCleanu
.build())
.build();
return partnerRepo.save(newPartner);
return partnerRepo.save(newPartner).load();
}).assertSuccessful().returnedValue();
}

View File

@ -473,7 +473,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTestWithClean
.anchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationRepo.save(partnerRel);
relationRepo.save(partnerRel).load();
return partnerRel;
}

View File

@ -332,7 +332,7 @@ class HsOfficePersonControllerAcceptanceTest extends ContextBasedTestWithCleanup
.givenName("Temp Given Name " + RandomStringUtils.randomAlphabetic(10))
.build();
return personRepo.save(newPerson);
return personRepo.save(newPerson).load();
}).assertSuccessful().returnedValue();
}

View File

@ -295,7 +295,8 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
final var found = relationRepo.findByUuid(saved.getUuid());
assertThat(found).isNotEmpty().get()
.isNotSameAs(saved)
.usingRecursiveComparison().ignoringFields("version").isEqualTo(saved);
.extracting(HsOfficeRelationEntity::toString)
.isEqualTo(saved.toString());
}
private void assertThatRelationIsVisibleForUserWithRole(

View File

@ -497,7 +497,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest extends ContextBasedTestWithCl
return jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
final var givenDebitor = debitorRepo.findDebitorByDebitorNumber(debitorNumber).get(0);
final var givenPartner = partnerRepo.findPartnerByDebitorUuid(givenDebitor.getUuid());
final var givenPartner = partnerRepo.findPartnerByPartnerNumber(debitorNumber/100);
final var bankAccountHolder = ofNullable(givenPartner.getPartnerRel().getHolder().getTradeName())
.orElse(givenPartner.getPartnerRel().getHolder().getFamilyName());
final var givenBankAccount = bankAccountRepo.findByOptionalHolderLike(bankAccountHolder).get(0);