Compare commits

..

2 Commits

Author SHA1 Message Date
Michael Hoennig
032ce6d16e fix HsOfficeDebitorEntityPatcherUnitTest 2024-02-20 14:06:05 +01:00
Michael Hoennig
45aab03d36 remove partner from debitor and use debitorRel instead, WIP with working EntityUnitTest 2024-02-20 13:03:17 +01:00
14 changed files with 304 additions and 254 deletions

View File

@ -3,7 +3,8 @@ package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
@ -11,9 +12,9 @@ import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@ -31,7 +32,7 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private static Stringify<HsOfficeDebitorEntity> stringify = private static Stringify<HsOfficeDebitorEntity> stringify =
stringify(HsOfficeDebitorEntity.class, "debitor") stringify(HsOfficeDebitorEntity.class, "debitor")
.withIdProp(HsOfficeDebitorEntity::toShortString) .withIdProp(HsOfficeDebitorEntity::toShortString)
.withProp(HsOfficeDebitorEntity::getPartner) .withProp(e -> ofNullable(e.getDebitorRel()).map(HsOfficeRelationshipEntity::toShortString).orElse(null))
.withProp(HsOfficeDebitorEntity::getDefaultPrefix) .withProp(HsOfficeDebitorEntity::getDefaultPrefix)
.quotedValues(false); .quotedValues(false);
@ -40,16 +41,12 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
@GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator") @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID uuid; private UUID uuid;
@ManyToOne
@JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner;
@Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)") @Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String? private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
@ManyToOne @ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "billingcontactuuid") @JoinColumn(name = "debitorreluuid", nullable = false)
private HsOfficeContactEntity billingContact; // TODO: migrate to billingPerson private HsOfficeRelationshipEntity debitorRel;
@Column(name = "billable", nullable = false) @Column(name = "billable", nullable = false)
private Boolean billable; // not a primitive because otherwise the default would be false private Boolean billable; // not a primitive because otherwise the default would be false
@ -74,14 +71,18 @@ public class HsOfficeDebitorEntity implements HasUuid, Stringifyable {
private String defaultPrefix; private String defaultPrefix;
private String getDebitorNumberString() { private String getDebitorNumberString() {
if (partner == null || partner.getPartnerNumber() == null || debitorNumberSuffix == null ) { return ofNullable(debitorRel)
return null; .filter(partnerNumber -> debitorNumberSuffix != null)
} .map(HsOfficeRelationshipEntity::getRelAnchor)
return partner.getPartnerNumber() + String.format("%02d", debitorNumberSuffix); .map(HsOfficePersonEntity::getOptionalPartner)
.map(HsOfficePartnerEntity::getPartnerNumber)
.map(Object::toString)
.map(partnerNumber -> partnerNumber + String.format("%02d", debitorNumberSuffix))
.orElse(null);
} }
public Integer getDebitorNumber() { public Integer getDebitorNumber() {
return Optional.ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null); return ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
} }
@Override @Override

View File

@ -1,8 +1,8 @@
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.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.EntityPatcher;
import net.hostsharing.hsadminng.mapper.OptionalFromJson; import net.hostsharing.hsadminng.mapper.OptionalFromJson;
@ -23,9 +23,9 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
@Override @Override
public void apply(final HsOfficeDebitorPatchResource resource) { public void apply(final HsOfficeDebitorPatchResource resource) {
OptionalFromJson.of(resource.getBillingContactUuid()).ifPresent(newValue -> { OptionalFromJson.of(resource.getDebitorRelUuid()).ifPresent(newValue -> {
verifyNotNull(newValue, "billingContact"); verifyNotNull(newValue, "debitorRel");
entity.setBillingContact(em.getReference(HsOfficeContactEntity.class, newValue)); entity.setDebitorRel(em.getReference(HsOfficeRelationshipEntity.class, newValue));
}); });
Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable); Optional.ofNullable(resource.getBillable()).ifPresent(entity::setBillable);
OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId); OptionalFromJson.of(resource.getVatId()).ifPresent(entity::setVatId);

View File

@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.person;
import lombok.*; import lombok.*;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.persistence.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -46,6 +47,14 @@ public class HsOfficePersonEntity implements HasUuid, Stringifyable {
@Column(name = "givenname") @Column(name = "givenname")
private String givenName; private String givenName;
@OneToOne(cascade = CascadeType.ALL)
@JoinTable(name = "hs_office_relationship",
joinColumns =
{ @JoinColumn(name = "uuid", referencedColumnName = "relanchoruuid") },
inverseJoinColumns =
{ @JoinColumn(name = "relanchoruuid", referencedColumnName = "uuid") })
private HsOfficePartnerEntity optionalPartner;
@Override @Override
public String toString() { public String toString() {
return toString.apply(this); return toString.apply(this);

View File

@ -43,7 +43,7 @@ components:
HsOfficeDebitorPatch: HsOfficeDebitorPatch:
type: object type: object
properties: properties:
billingContactUuid: debitorRelUuid:
type: string type: string
format: uuid format: uuid
nullable: true nullable: true

View File

@ -7,10 +7,9 @@
create table hs_office_debitor create table hs_office_debitor
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
partnerUuid uuid not null references hs_office_partner(uuid),
billable boolean not null default true,
debitorNumberSuffix numeric(2) not null, debitorNumberSuffix numeric(2) not null,
billingContactUuid uuid not null references hs_office_contact(uuid), debitorRelUuid uuid not null references hs_office_relationship(uuid),
billable boolean not null default true,
vatId varchar(24), -- TODO.spec: here or in person? vatId varchar(24), -- TODO.spec: here or in person?
vatCountryCode varchar(2), vatCountryCode varchar(2),
vatBusiness boolean not null, vatBusiness boolean not null,

View File

@ -7,13 +7,6 @@ call generateRelatedRbacObject('hs_office_debitor');
--// --//
-- ============================================================================
--changeset hs-office-debitor-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRoleDescriptors('hsOfficeDebitor', 'hs_office_debitor');
--//
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--// --changeset hs-office-debitor-rbac-ROLES-CREATION:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
@ -27,121 +20,103 @@ create or replace function hsOfficeDebitorRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
hsOfficeDebitorTenant RbacRoleDescriptor; debitorUuid uuid;
oldPartner hs_office_partner;
newPartner hs_office_partner; oldDebitorRel hs_office_relationship;
oldPartnerRel hs_office_relationship; newDebitorRel hs_office_relationship;
newPartnerRel hs_office_relationship;
oldContact hs_office_contact; newPartnerRel hs_office_relationship;
newContact hs_office_contact;
newBankAccount hs_office_bankaccount; newBankAccount hs_office_bankaccount;
oldBankAccount hs_office_bankaccount; oldBankAccount hs_office_bankaccount;
begin begin
call enterTriggerForObjectUuid(NEW.uuid); call enterTriggerForObjectUuid(NEW.uuid);
hsOfficeDebitorTenant := hsOfficeDebitorTenant(NEW); debitorUuid := NEW.uuid;
select * from hs_office_partner as p where p.uuid = NEW.partnerUuid into newPartner; select * into newDebitorRel
select * from hs_office_relationship as r where r.relType = 'PARTNER' and r.relHolderUuid = NEW.partnerUuid into newPartnerRel; from hs_office_relationship as r where r.relType = 'DEBITOR' and r.relHolderUuid = NEW.debitorRelUuid;
select * from hs_office_contact as c where c.uuid = NEW.billingContactUuid into newContact;
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount; select * into newPartnerRel
from hs_office_relationship as r
join hs_office_partner as p on p.partnerRoleUuid = r.uuid
where r.relType = 'PARTNER' and r.relHolderUuid = newPartnerRel;
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid
into newBankAccount;
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
perform createRoleWithGrants( -- Permissions and Grants for Debitor
hsOfficeDebitorOwner(NEW),
permissions => array['*'],
incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
);
perform createRoleWithGrants( call grantPermissionsToRole(
hsOfficeDebitorAdmin(NEW), getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
permissions => array['edit'], createPermissions(partnerUuid, array ['*'])
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)] );
);
perform createRoleWithGrants( call grantPermissionsToRole(
hsOfficeDebitorAgent(NEW), getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
incomingSuperRoles => array[ createPermissions(partnerUuid, array ['edit'])
hsOfficeDebitorAdmin(NEW), );
hsOfficeRelationshipAdmin(newPartnerRel),
hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[
hsOfficeBankAccountTenant(newBankaccount)]
);
perform createRoleWithGrants( call grantPermissionsToRole(
hsOfficeDebitorTenant(NEW), getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
incomingSuperRoles => array[ createPermissions(partnerUuid, array ['view'])
hsOfficeDebitorAgent(NEW), );
hsOfficeRelationshipAgent(newPartnerRel),
hsOfficeBankAccountAdmin(newBankaccount)],
outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRel),
hsOfficeContactReferrer(newContact),
hsOfficeBankAccountGuest(newBankaccount)]
);
perform createRoleWithGrants( -- Grants to and from related Partner Relationship
hsOfficeDebitorGuest(NEW),
permissions => array['view'], call grantRoleToRole(hsOfficeRelationshipAdmin(newDebitorRel), hsOfficeRelationshipAdmin(newPartnerRel), true);
incomingSuperRoles => array[ call grantRoleToRole(hsOfficeRelationshipAgent(newPartnerRel), hsOfficeRelationshipAdmin(newDebitorRel), true);
hsOfficeDebitorTenant(NEW)]
); call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeRelationshipAgent(newPartnerRel), true);
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeRelationshipAgent(newDebitorRel), true);
-- Grants to and from refundBankAccount
if newBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
end if;
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.partnerUuid <> NEW.partnerUuid then if OLD.debitorRelUuid is distinct from NEW.debitorRelUuid then
select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner;
select * from hs_office_relationship as r where r.uuid = OLD.partnerUuid into oldPartnerRel;
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeRelationshipAdmin(oldPartnerRel)); select * into oldDebitorRel
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeRelationshipAdmin(oldPartnerRel)); from hs_office_relationship as r where r.relType = 'DEBITOR' and r.relHolderUuid = NEW.debitorRelUuid;
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeRelationshipAgent(oldPartnerRel)); call grantPermissionsToRole(
call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeRelationshipAgent(newPartner)); getRoleId(hsOfficeRelationshipOwner(newDebitorRel), 'fail'),
createPermissions(partnerUuid, array ['*'])
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipAdmin(newDebitorRel), 'fail'),
createPermissions(partnerUuid, array ['edit'])
);
call grantPermissionsToRole(
getRoleId(hsOfficeRelationshipTenant(newDebitorRel), 'fail'),
createPermissions(partnerUuid, array ['view'])
);
call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPartnerRel), hsOfficeDebitorTenant(OLD));
call grantRoleToRole(hsOfficeRelationshipTenant(newPartnerRel), hsOfficeDebitorTenant(NEW));
end if; end if;
if OLD.billingContactUuid <> NEW.billingContactUuid then if OLD.refundBankAccountUuid is distinct from NEW.refundBankAccountUuid then
select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact;
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeContactAdmin(oldContact)); select * into oldBankAccount
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeContactAdmin(newContact)); from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid;
call revokeRoleFromRole(hsOfficeContactReferrer(oldContact), hsOfficeDebitorTenant(OLD));
call grantRoleToRole(hsOfficeContactReferrer(newContact), hsOfficeDebitorTenant(NEW));
end if;
if (OLD.refundBankAccountUuid is not null or NEW.refundBankAccountUuid is not null) and
( OLD.refundBankAccountUuid is null or NEW.refundBankAccountUuid is null or
OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid ) then
select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount;
if oldBankAccount is not null then if oldBankAccount is not null then
call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankaccount), hsOfficeDebitorAgent(OLD)); call revokeRoleFromRole(hsOfficeBankAccountReferrer(oldBankAccount), hsOfficeRelationshipAgent(oldDebitorRel), true);
end if; call revokeRoleFromRole(hsOfficeRelationshipAgent(oldDebitorRel), hsOfficeBankAccountAdmin(oldBankAccount), true);
if newBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountTenant(newBankaccount), hsOfficeDebitorAgent(NEW));
end if; end if;
if oldBankAccount is not null then
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeBankAccountAdmin(oldBankaccount));
end if;
if newBankAccount is not null then if newBankAccount is not null then
call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeBankAccountAdmin(newBankaccount)); call grantRoleToRole(hsOfficeBankAccountReferrer(newBankAccount), hsOfficeRelationshipAgent(newDebitorRel), true);
end if; call grantRoleToRole(hsOfficeRelationshipAgent(newDebitorRel), hsOfficeBankAccountAdmin(newBankAccount), true);
if oldBankAccount is not null then
call revokeRoleFromRole(hsOfficeBankAccountGuest(oldBankaccount), hsOfficeDebitorTenant(OLD));
end if;
if newBankAccount is not null then
call grantRoleToRole(hsOfficeBankAccountGuest(newBankaccount), hsOfficeDebitorTenant(NEW));
end if; end if;
end if; end if;
else else
@ -172,6 +147,37 @@ execute procedure hsOfficeDebitorRbacRolesTrigger();
--// --//
/*
Creates and updates the roles and their assignments for debitor entities if partner rel changes.
*/
create or replace function hsOfficeDebitorPartnerRelRbacRolesTrigger()
returns trigger
language plpgsql
strict as $$
declare
begin
call enterTriggerForObjectUuid(NEW.uuid);
-- TODO
call leaveTriggerForObjectUuid(NEW.uuid);
return NEW;
end; $$;
--//
/*
An AFTER UPDATE TRIGGER which creates the role structure for debitors if partner relations change.
*/
create trigger updateRbacRolesForHsOfficeDebitor_Trigger
after update
on hs_office_partner
for each row
execute procedure hsOfficeDebitorPartnerRelRbacRolesTrigger();
--//
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------

View File

@ -410,19 +410,20 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
.body("vatBusiness", is(true)) .body("vatBusiness", is(true))
.body("defaultPrefix", is("for")) .body("defaultPrefix", is("for"))
.body("billingContact.label", is(givenContact.getLabel())) .body("billingContact.label", is(givenContact.getLabel()))
.body("partner.partnerRole.relHolder.tradeName", is(givenDebitor.getPartner().getPartnerRole().getRelHolder().getTradeName())); // TODO .body("partner.partnerRole.relHolder.tradeName", is(givenDebitor.getPartner().getPartnerRole().getRelHolder().getTradeName()))
;
// @formatter:on // @formatter:on
// finally, the debitor is actually updated // finally, the debitor is actually updated
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get() assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
.matches(partner -> { .matches(debitor -> {
assertThat(partner.getPartner().getPartnerRole().getRelHolder().getTradeName()) assertThat(debitor.getDebitorRel().getRelHolder().getTradeName())
.isEqualTo(givenDebitor.getPartner().getPartnerRole().getRelHolder().getTradeName()); .isEqualTo(givenDebitor.getDebitorRel().getRelHolder().getTradeName());
assertThat(partner.getBillingContact().getLabel()).isEqualTo("fourth contact"); assertThat(debitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
assertThat(partner.getVatId()).isEqualTo("VAT222222"); assertThat(debitor.getVatId()).isEqualTo("VAT222222");
assertThat(partner.getVatCountryCode()).isEqualTo("AA"); assertThat(debitor.getVatCountryCode()).isEqualTo("AA");
assertThat(partner.isVatBusiness()).isEqualTo(true); assertThat(debitor.isVatBusiness()).isEqualTo(true);
return true; return true;
}); });
} }
@ -460,9 +461,9 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
// finally, the debitor is actually updated // finally, the debitor is actually updated
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get() assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent().get()
.matches(partner -> { .matches(partner -> {
assertThat(partner.getPartner().getPartnerRole().getRelHolder().getTradeName()) assertThat(partner.getDebitorRel().getRelHolder().getTradeName())
.isEqualTo(givenDebitor.getPartner().getPartnerRole().getRelHolder().getTradeName()); .isEqualTo(givenDebitor.getDebitorRel().getRelHolder().getTradeName());
assertThat(partner.getBillingContact().getLabel()).isEqualTo("sixth contact"); assertThat(partner.getDebitorRel().getContact().getLabel()).isEqualTo("sixth contact");
assertThat(partner.getVatId()).isEqualTo("VAT999999"); assertThat(partner.getVatId()).isEqualTo("VAT999999");
assertThat(partner.getVatCountryCode()).isEqualTo(givenDebitor.getVatCountryCode()); assertThat(partner.getVatCountryCode()).isEqualTo(givenDebitor.getVatCountryCode());
assertThat(partner.isVatBusiness()).isEqualTo(givenDebitor.isVatBusiness()); assertThat(partner.isVatBusiness()).isEqualTo(givenDebitor.isVatBusiness());
@ -499,7 +500,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
void contactAdminUser_canNotDeleteRelatedDebitor() { void contactAdminUser_canNotDeleteRelatedDebitor() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor(); final var givenDebitor = givenSomeTemporaryDebitor();
assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact"); assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -519,7 +520,7 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
void normalUser_canNotDeleteUnrelatedDebitor() { void normalUser_canNotDeleteUnrelatedDebitor() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor(); final var givenDebitor = givenSomeTemporaryDebitor();
assertThat(givenDebitor.getBillingContact().getLabel()).isEqualTo("fourth contact"); assertThat(givenDebitor.getDebitorRel().getContact().getLabel()).isEqualTo("fourth contact");
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -543,8 +544,8 @@ class HsOfficeDebitorControllerAcceptanceTest extends ContextBasedTestWithCleanu
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(++nextDebitorSuffix) .debitorNumberSuffix(++nextDebitorSuffix)
.billable(true) .billable(true)
.partner(givenPartner) // .partner(givenPartner)
.billingContact(givenContact) // .billingContact(givenContact)
.defaultPrefix("abc") .defaultPrefix("abc")
.vatReverseCharge(false) .vatReverseCharge(false)
.build(); .build();

View File

@ -1,9 +1,8 @@
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.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebitorPatchResource;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.test.PatchUnitTestBase; import net.hostsharing.test.PatchUnitTestBase;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestInstance;
@ -28,9 +27,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
> { > {
private static final UUID INITIAL_DEBITOR_UUID = UUID.randomUUID(); private static final UUID INITIAL_DEBITOR_UUID = UUID.randomUUID();
private static final UUID INITIAL_PARTNER_UUID = UUID.randomUUID(); private static final UUID INITIAL_DEBITOR_REL_UUID = UUID.randomUUID();
private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); private static final UUID PATCHED_DEBITOR_REL_UUID = UUID.randomUUID();
private static final UUID PATCHED_CONTACT_UUID = UUID.randomUUID();
private static final String PATCHED_DEFAULT_PREFIX = "xyz"; private static final String PATCHED_DEFAULT_PREFIX = "xyz";
private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ"; private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ";
@ -46,12 +44,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
private static final UUID INITIAL_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID(); private static final UUID INITIAL_REFUND_BANK_ACCOUNT_UUID = UUID.randomUUID();
private static final UUID PATCHED_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 HsOfficeRelationshipEntity givenInitialDebitorRel = HsOfficeRelationshipEntity.builder()
.uuid(INITIAL_PARTNER_UUID) .uuid(INITIAL_DEBITOR_REL_UUID)
.build();
private final HsOfficeContactEntity givenInitialContact = HsOfficeContactEntity.builder()
.uuid(INITIAL_CONTACT_UUID)
.build(); .build();
private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder() private final HsOfficeBankAccountEntity givenInitialBankAccount = HsOfficeBankAccountEntity.builder()
@ -62,8 +56,8 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
@BeforeEach @BeforeEach
void initMocks() { void initMocks() {
lenient().when(em.getReference(eq(HsOfficeContactEntity.class), any())).thenAnswer(invocation -> lenient().when(em.getReference(eq(HsOfficeRelationshipEntity.class), any())).thenAnswer(invocation ->
HsOfficeContactEntity.builder().uuid(invocation.getArgument(1)).build()); HsOfficeRelationshipEntity.builder().uuid(invocation.getArgument(1)).build());
lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation -> lenient().when(em.getReference(eq(HsOfficeBankAccountEntity.class), any())).thenAnswer(invocation ->
HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build()); HsOfficeBankAccountEntity.builder().uuid(invocation.getArgument(1)).build());
} }
@ -72,8 +66,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
protected HsOfficeDebitorEntity newInitialEntity() { protected HsOfficeDebitorEntity newInitialEntity() {
final var entity = new HsOfficeDebitorEntity(); final var entity = new HsOfficeDebitorEntity();
entity.setUuid(INITIAL_DEBITOR_UUID); entity.setUuid(INITIAL_DEBITOR_UUID);
entity.setPartner(givenInitialPartner); entity.setDebitorRel(givenInitialDebitorRel);
entity.setBillingContact(givenInitialContact);
entity.setBillable(INITIAL_BILLABLE); entity.setBillable(INITIAL_BILLABLE);
entity.setVatId("initial VAT-ID"); entity.setVatId("initial VAT-ID");
entity.setVatCountryCode("AA"); entity.setVatCountryCode("AA");
@ -98,11 +91,11 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
protected Stream<Property> propertyTestDescriptors() { protected Stream<Property> propertyTestDescriptors() {
return Stream.of( return Stream.of(
new JsonNullableProperty<>( new JsonNullableProperty<>(
"billingContact", "debitorRel",
HsOfficeDebitorPatchResource::setBillingContactUuid, HsOfficeDebitorPatchResource::setDebitorRelUuid,
PATCHED_CONTACT_UUID, PATCHED_DEBITOR_REL_UUID,
HsOfficeDebitorEntity::setBillingContact, HsOfficeDebitorEntity::setDebitorRel,
newBillingContact(PATCHED_CONTACT_UUID)) newDebitorRel(PATCHED_DEBITOR_REL_UUID))
.notNullable(), .notNullable(),
new SimpleProperty<>( new SimpleProperty<>(
"billable", "billable",
@ -129,7 +122,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
new SimpleProperty<>( new SimpleProperty<>(
"vatReverseCharge", "vatReverseCharge",
HsOfficeDebitorPatchResource::setVatReverseCharge, HsOfficeDebitorPatchResource::setVatReverseCharge,
PATCHED_BILLABLE, PATCHED_VAT_REVERSE_CHARGE,
HsOfficeDebitorEntity::setVatReverseCharge) HsOfficeDebitorEntity::setVatReverseCharge)
.notNullable(), .notNullable(),
new JsonNullableProperty<>( new JsonNullableProperty<>(
@ -148,15 +141,15 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
); );
} }
private HsOfficeContactEntity newBillingContact(final UUID uuid) { private HsOfficeRelationshipEntity newDebitorRel(final UUID uuid) {
final var newContact = new HsOfficeContactEntity(); return HsOfficeRelationshipEntity.builder()
newContact.setUuid(uuid); .uuid(uuid)
return newContact; .build();
} }
private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) { private HsOfficeBankAccountEntity newBankAccount(final UUID uuid) {
final var newBankAccount = new HsOfficeBankAccountEntity(); return HsOfficeBankAccountEntity.builder()
newBankAccount.setUuid(uuid); .uuid(uuid)
return newBankAccount; .build();
} }
} }

View File

@ -1,7 +1,6 @@
package net.hostsharing.hsadminng.hs.office.debitor; package net.hostsharing.hsadminng.hs.office.debitor;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerDetailsEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
@ -12,52 +11,38 @@ import static org.assertj.core.api.Assertions.assertThat;
class HsOfficeDebitorEntityUnitTest { class HsOfficeDebitorEntityUnitTest {
private HsOfficeRelationshipEntity givenDebitorRel = HsOfficeRelationshipEntity.builder()
.relAnchor(HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("some partner trade name")
.optionalPartner(HsOfficePartnerEntity.builder()
.partnerNumber(12345)
.build())
.build())
.relHolder(HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("some billing trade name")
.build())
.contact(HsOfficeContactEntity.builder().label("some label").build())
.build();
@Test @Test
void toStringContainsPartnerAndContact() { void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)67) .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder() .debitorRel(givenDebitorRel)
.partnerRole(HsOfficeRelationshipEntity.builder()
.relHolder(HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.LEGAL_PERSON)
.tradeName("some trade name")
.build())
.build())
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
.partnerNumber(12345)
.build())
.billingContact(HsOfficeContactEntity.builder().label("some label").build())
.defaultPrefix("som") .defaultPrefix("som")
.build(); .build();
final var result = given.toString(); final var result = given.toString();
assertThat(result).isEqualTo("debitor(D-1234567: P-12345, som)"); assertThat(result).isEqualTo("debitor(D-1234567: rel(relAnchor='LP some partner trade name', relHolder='LP some billing trade name'), som)");
}
@Test
void toStringWithoutPersonContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
.partnerRole(null)
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
.partnerNumber(12345)
.build())
.billingContact(HsOfficeContactEntity.builder().label("some label").build())
.build();
final var result = given.toString();
assertThat(result).isEqualTo("debitor(D-1234567: P-12345)");
} }
@Test @Test
void toShortStringContainsDebitorNumber() { void toShortStringContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder() .debitorRel(givenDebitorRel)
.partnerNumber(12345)
.build())
.debitorNumberSuffix((byte)67) .debitorNumberSuffix((byte)67)
.build(); .build();
@ -69,9 +54,7 @@ class HsOfficeDebitorEntityUnitTest {
@Test @Test
void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() { void getDebitorNumberWithPartnerNumberAndDebitorNumberSuffix() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder() .debitorRel(givenDebitorRel)
.partnerNumber(12345)
.build())
.debitorNumberSuffix((byte)67) .debitorNumberSuffix((byte)67)
.build(); .build();
@ -82,8 +65,9 @@ class HsOfficeDebitorEntityUnitTest {
@Test @Test
void getDebitorNumberWithoutPartnerReturnsNull() { void getDebitorNumberWithoutPartnerReturnsNull() {
givenDebitorRel.getRelAnchor().setOptionalPartner(null);
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.partner(null) .debitorRel(givenDebitorRel)
.debitorNumberSuffix((byte)67) .debitorNumberSuffix((byte)67)
.build(); .build();
@ -94,10 +78,9 @@ class HsOfficeDebitorEntityUnitTest {
@Test @Test
void getDebitorNumberWithoutPartnerNumberReturnsNull() { void getDebitorNumberWithoutPartnerNumberReturnsNull() {
givenDebitorRel.getRelAnchor().getOptionalPartner().setPartnerNumber(null);
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder() .debitorRel(givenDebitorRel)
.partnerNumber(null)
.build())
.debitorNumberSuffix((byte)67) .debitorNumberSuffix((byte)67)
.build(); .build();
@ -109,9 +92,7 @@ class HsOfficeDebitorEntityUnitTest {
@Test @Test
void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() { void getDebitorNumberWithoutDebitorNumberSuffixReturnsNull() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder() .debitorRel(givenDebitorRel)
.partnerNumber(12345)
.build())
.debitorNumberSuffix(null) .debitorNumberSuffix(null)
.build(); .build();

View File

@ -4,8 +4,13 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRepository;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerRepository;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup; import net.hostsharing.hsadminng.hs.office.test.ContextBasedTestWithCleanup;
import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository; import net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantRepository;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService;
import net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsMermaidService.Include;
import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository; import net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleRepository;
import net.hostsharing.test.Array; import net.hostsharing.test.Array;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
@ -25,15 +30,17 @@ 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.EnumSet;
import java.util.List; import java.util.List;
import static net.hostsharing.hsadminng.hs.office.test.EntityList.one;
import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf; import static net.hostsharing.hsadminng.rbac.rbacgrant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf; import static net.hostsharing.hsadminng.rbac.rbacrole.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.test.JpaAttempt.attempt; import static net.hostsharing.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@DataJpaTest @DataJpaTest
@Import( { Context.class, JpaAttempt.class }) @Import( { Context.class, JpaAttempt.class, RbacGrantsMermaidService.class })
class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup { class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
@Autowired @Autowired
@ -45,6 +52,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired @Autowired
HsOfficeContactRepository contactRepo; HsOfficeContactRepository contactRepo;
@Autowired
HsOfficePersonRepository personRepo;
@Autowired @Autowired
HsOfficeBankAccountRepository bankAccountRepo; HsOfficeBankAccountRepository bankAccountRepo;
@ -60,9 +70,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
@Autowired @Autowired
JpaAttempt jpaAttempt; JpaAttempt jpaAttempt;
@Autowired
RbacGrantsMermaidService mermaidService;
@MockBean @MockBean
HttpServletRequest request; HttpServletRequest request;
@Nested @Nested
class CreateDebitor { class CreateDebitor {
@ -71,15 +83,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = debitorRepo.count(); final var count = debitorRepo.count();
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)21) .debitorNumberSuffix((byte)21)
.partner(givenPartner) .debitorRel(HsOfficeRelationshipEntity.builder()
.billingContact(givenContact) .relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenPartnerPerson)
.relHolder(givenPartnerPerson)
.contact(givenContact)
.build())
.defaultPrefix("abc") .defaultPrefix("abc")
.billable(false) .billable(false)
.build(); .build();
@ -99,16 +115,19 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) { public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = debitorRepo.count(); final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0); final var givenContact = one(contactRepo.findContactByOptionalLabelLike("first contact"));
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)21) .debitorNumberSuffix((byte)21)
.partner(givenPartner) .debitorRel(HsOfficeRelationshipEntity.builder()
.billingContact(givenContact) .relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenPartnerPerson)
.relHolder(givenPartnerPerson)
.contact(givenContact)
.build())
.billable(true) .billable(true)
.vatReverseCharge(false) .vatReverseCharge(false)
.vatBusiness(false) .vatBusiness(false)
@ -119,7 +138,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// then // then
System.out.println("ok"); System.out.println("ok");
// result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class); // TODO result.assertExceptionWithRootCauseMessage(org.hibernate.exception.ConstraintViolationException.class);
} }
@Test @Test
@ -138,12 +157,16 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when // when
attempt(em, () -> { attempt(em, () -> {
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0); final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First GmbH"));
final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth contact").get(0); final var givenContact = one(contactRepo.findContactByOptionalLabelLike("fourth contact"));
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)22) .debitorNumberSuffix((byte)22)
.partner(givenPartner) .debitorRel(HsOfficeRelationshipEntity.builder()
.billingContact(givenContact) .relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenPartnerPerson)
.relHolder(givenPartnerPerson)
.contact(givenContact)
.build())
.defaultPrefix("abc") .defaultPrefix("abc")
.billable(false) .billable(false)
.build(); .build();
@ -178,13 +201,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// agent // agent
"{ grant role debitor#1000422:FeG.agent to role debitor#1000422:FeG.admin by system and assume }", "{ grant role debitor#1000422:FeG.agent to role debitor#1000422:FeG.admin by system and assume }",
"{ grant role debitor#1000422:FeG.agent to role contact#4th.admin by system and assume }", "{ grant role debitor#1000422:FeG.agent to role contact#4th.admin by system and assume }",
"{ grant role debitor#1000422:FeG.agent to role partner#10004:FeG.admin by system and assume }", // "{ grant role debitor#1000422:FeG.agent to role partner#10004:FeG.admin by system and assume }",
// tenant // tenant
"{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }", //"{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }",
"{ grant role debitor#1000422:FeG.tenant to role debitor#1000422:FeG.agent by system and assume }", "{ grant role debitor#1000422:FeG.tenant to role debitor#1000422:FeG.agent by system and assume }",
"{ grant role debitor#1000422:FeG.tenant to role partner#10004:FeG.agent by system and assume }", //"{ grant role debitor#1000422:FeG.tenant to role partner#10004:FeG.agent by system and assume }",
"{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }", //"{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
"{ grant role contact#4th.referrer to role debitor#1000422:FeG.tenant by system and assume }",
// guest // guest
"{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }", "{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
@ -291,13 +315,17 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
RbacGrantsMermaidService.writeToFile("initial partner: Fourth eG + fourth contact",
mermaidService.allGrantsFrom(givenDebitor.getUuid(), "view", EnumSet.of(Include.USERS, Include.DETAILS)),
"doc/all-grants-before-globalAdmin_canUpdateArbitraryDebitor.md");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_partner#10004:FourtheG-fourthcontact.admin"); "hs_office_debitor#1000420:FourtheG-fourthcontact.agent");
assertThatDebitorActuallyInDatabase(givenDebitor); final var givenNewPartnerPerson = one(personRepo.findPersonByOptionalNameLike("First"));
final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenNewContact = one(contactRepo.findContactByOptionalLabelLike("sixth contact"));
final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0); final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
final String givenNewVatId = "NEW-VAT-ID"; final String givenNewVatId = "NEW-VAT-ID";
final String givenNewVatCountryCode = "NC"; final String givenNewVatCountryCode = "NC";
final boolean givenNewVatBusiness = !givenDebitor.isVatBusiness(); final boolean givenNewVatBusiness = !givenDebitor.isVatBusiness();
@ -305,8 +333,12 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
givenDebitor.setPartner(givenNewPartner); givenDebitor.setDebitorRel(HsOfficeRelationshipEntity.builder()
givenDebitor.setBillingContact(givenNewContact); .relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenNewPartnerPerson)
.relHolder(givenNewPartnerPerson)
.contact(givenNewContact)
.build());
givenDebitor.setRefundBankAccount(givenNewBankAccount); givenDebitor.setRefundBankAccount(givenNewBankAccount);
givenDebitor.setVatId(givenNewVatId); givenDebitor.setVatId(givenNewVatId);
givenDebitor.setVatCountryCode(givenNewVatCountryCode); givenDebitor.setVatCountryCode(givenNewVatCountryCode);
@ -354,7 +386,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
givenDebitor, givenDebitor,
"hs_office_partner#10004:FourtheG-fourthcontact.admin"); "hs_office_partner#10004:FourtheG-fourthcontact.admin");
assertThatDebitorActuallyInDatabase(givenDebitor); assertThatDebitorActuallyInDatabase(givenDebitor);
final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0); final var givenNewBankAccount = one(bankAccountRepo.findByOptionalHolderLike("first"));
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -498,14 +530,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
} }
@Test @Test
public void relatedPerson_canNotDeleteTheirRelatedDebitor() { public void debitorAgent_canViewButNotDeleteTheirRelatedDebitor() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("person-FourtheG@example.com"); context("superuser-alex@hostsharing.net", "hs_office_debitor#1000420:FourtheG-fourthcontact.agent");
assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent(); assertThat(debitorRepo.findByUuid(givenDebitor.getUuid())).isPresent();
debitorRepo.deleteByUuid(givenDebitor.getUuid()); debitorRepo.deleteByUuid(givenDebitor.getUuid());
@ -562,20 +594,24 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTestWithClean
} }
private HsOfficeDebitorEntity givenSomeTemporaryDebitor( private HsOfficeDebitorEntity givenSomeTemporaryDebitor(
final String partner, final String partnerName,
final String contact, final String contactLabel,
final String bankAccount, final String bankAccountHolder,
final String defaultPrefix) { final String defaultPrefix) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike(partner).get(0); final var givenPartnerPerson = one(personRepo.findPersonByOptionalNameLike(partnerName));
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = one(contactRepo.findContactByOptionalLabelLike(contactLabel));
final var givenBankAccount = final var givenBankAccount =
bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null; bankAccountHolder != null ? one(bankAccountRepo.findByOptionalHolderLike(bankAccountHolder)) : null;
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)20) .debitorNumberSuffix((byte)20)
.partner(givenPartner) .debitorRel(HsOfficeRelationshipEntity.builder()
.billingContact(givenContact) .relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(givenPartnerPerson)
.relHolder(givenPartnerPerson)
.contact(givenContact)
.build())
.refundBankAccount(givenBankAccount) .refundBankAccount(givenBankAccount)
.defaultPrefix(defaultPrefix) .defaultPrefix(defaultPrefix)
.billable(true) .billable(true)

View File

@ -1,7 +1,8 @@
package net.hostsharing.hsadminng.hs.office.debitor; package net.hostsharing.hsadminng.hs.office.debitor;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity;
import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT; import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT;
import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER; import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TEST_PARTNER;
@ -13,7 +14,12 @@ public class TestHsOfficeDebitor {
public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder() public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX) .debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
.partner(TEST_PARTNER) .debitorRel(HsOfficeRelationshipEntity.builder()
.billingContact(TEST_CONTACT) .relHolder(HsOfficePersonEntity.builder().build())
.relAnchor(HsOfficePersonEntity.builder()
.optionalPartner(TEST_PARTNER)
.build())
.contact(TEST_CONTACT)
.build())
.build(); .build();
} }

View File

@ -445,8 +445,7 @@ public class ImportOfficeData extends ContextBasedTest {
final var idsToRemove = new HashSet<Integer>(); final var idsToRemove = new HashSet<Integer>();
debitors.forEach( (id, d) -> { debitors.forEach( (id, d) -> {
// such a record is in test data to test error messages // such a record is in test data to test error messages
if (d.getBillingContact() == null || d.getBillingContact().getLabel() == null || if (false) { // TODO: how can I now empty debitors?
d.getPartner() == null || d.getPartner().getPartnerRole().getRelAnchor().getPersonType() == null ) {
idsToRemove.add(id); idsToRemove.add(id);
} }
}); });
@ -676,10 +675,15 @@ public class ImportOfficeData extends ContextBasedTest {
partners.put(rec.getInteger("bp_id"), partner); partners.put(rec.getInteger("bp_id"), partner);
final var debitor = HsOfficeDebitorEntity.builder() final var debitor = HsOfficeDebitorEntity.builder()
.partner(partner)
.debitorNumberSuffix((byte) 0) .debitorNumberSuffix((byte) 0)
.debitorRel(
HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.ACCOUNTING)
.relAnchor(partnerRelationship.getRelHolder())
.relHolder(null) // gets set later
.build()
)
.defaultPrefix(rec.getString("member_code").replace("hsh00-", "")) .defaultPrefix(rec.getString("member_code").replace("hsh00-", ""))
.partner(partner)
.billable(rec.isEmpty("free") || rec.getString("free").equals("f")) .billable(rec.isEmpty("free") || rec.getString("free").equals("f"))
.vatReverseCharge(rec.getBoolean("exempt_vat")) .vatReverseCharge(rec.getBoolean("exempt_vat"))
.vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove
@ -846,8 +850,8 @@ public class ImportOfficeData extends ContextBasedTest {
partner.getPartnerRole().setContact(contact); partner.getPartnerRole().setContact(contact);
} }
if (containsRole(rec, "billing")) { if (containsRole(rec, "billing")) {
assertThat(debitor.getBillingContact()).isNull(); assertThat(debitor.getDebitorRel().getContact()).isNull();
debitor.setBillingContact(contact); debitor.getDebitorRel().setContact(contact);
} }
if (containsRole(rec, "operation")) { if (containsRole(rec, "operation")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.OPERATIONS); addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.OPERATIONS);

View File

@ -19,7 +19,6 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.EnumSet;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid; import static net.hostsharing.test.IsValidUuidMatcher.isUuidValid;

View File

@ -0,0 +1,15 @@
package net.hostsharing.hsadminng.hs.office.test;
import net.hostsharing.hsadminng.persistence.HasUuid;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class EntityList {
public static <E extends HasUuid> E one(final List<E> entities) {
assertThat(entities).hasSize(1);
return entities.stream().findFirst().orElseThrow();
}
}