db-migration #10

Merged
hsh-michaelhoennig merged 74 commits from db-migration into master 2024-01-23 15:11:24 +01:00
39 changed files with 435 additions and 260 deletions
Showing only changes of commit 980524e7ab - Show all commits

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact;
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.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.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;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopassets;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.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;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.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;

View File

@ -40,13 +40,16 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@JoinColumn(name = "partneruuid") @JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner; private HsOfficePartnerEntity partner;
@Column(name = "debitornumber") @Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
private Integer debitorNumber; private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
@ManyToOne @ManyToOne
@JoinColumn(name = "billingcontactuuid") @JoinColumn(name = "billingcontactuuid")
private HsOfficeContactEntity billingContact; private HsOfficeContactEntity billingContact;
@Column(name = "billable")
private boolean billable;
@Column(name = "vatid") @Column(name = "vatid")
private String vatId; private String vatId;
@ -60,6 +63,17 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@JoinColumn(name = "refundbankaccountuuid") @JoinColumn(name = "refundbankaccountuuid")
private HsOfficeBankAccountEntity refundBankAccount; private HsOfficeBankAccountEntity refundBankAccount;
public String getDebitorNumberString() {
return partner.getDebitorNumberPrefix() + String.format("%02d", debitorNumberSuffix);
}
public int getDebitorNumber() {
return Integer.parseInt(getDebitorNumberString());
}
@Column(name = "defaultprefix", columnDefinition = "char(3) not null")
private String defaultPrefix;
@Override @Override
public String toString() { public String toString() {
return stringify.apply(this); return stringify.apply(this);
@ -67,6 +81,6 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@Override @Override
public String toShortString() { public String toShortString() {
return debitorNumber.toString(); return getDebitorNumberString();
} }
} }

View File

@ -31,6 +31,10 @@ class HsOfficeDebitorEntityPatcher implements EntityPatcher<HsOfficeDebitorPatch
verifyNotNull(newValue, "vatBusiness"); verifyNotNull(newValue, "vatBusiness");
entity.setVatBusiness(newValue); entity.setVatBusiness(newValue);
}); });
OptionalFromJson.of(resource.getDefaultPrefix()).ifPresent(newValue -> {
verifyNotNull(newValue, "defaultPrefix");
entity.setDefaultPrefix(newValue);
});
} }
private void verifyNotNull(final Object newValue, final String propertyName) { private void verifyNotNull(final Object newValue, final String propertyName) {

View File

@ -13,9 +13,14 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query(""" @Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor SELECT debitor FROM HsOfficeDebitorEntity debitor
WHERE debitor.debitorNumber = :debitorNumber WHERE cast(debitor.partner.debitorNumberPrefix as integer) = :debitorNumberPrefix
AND cast(debitor.debitorNumberSuffix as integer) = :debitorNumberSuffix
""") """)
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumber); List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumberPrefix, byte debitorNumberSuffix);
default List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumber) {
return findDebitorByDebitorNumber( debitorNumber/100, (byte) (debitorNumber%100));
}
@Query(""" @Query("""
SELECT debitor FROM HsOfficeDebitorEntity debitor SELECT debitor FROM HsOfficeDebitorEntity debitor

View File

@ -58,6 +58,9 @@ public class HsOfficeMembershipEntity implements Stringifyable {
@Type(PostgreSQLRangeType.class) @Type(PostgreSQLRangeType.class)
private Range<LocalDate> validity; private Range<LocalDate> validity;
@Column(name = "membership_fee_billable")
private boolean membershipFeeBillable;
@Column(name = "reasonfortermination") @Column(name = "reasonfortermination")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private HsOfficeReasonForTermination reasonForTermination; private HsOfficeReasonForTermination reasonForTermination;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.repository; package net.hostsharing.hsadminng.hs.office.migration;
import java.util.UUID; import java.util.UUID;

View File

@ -3,14 +3,15 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.repository.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;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.NotFoundAction;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@ -35,6 +36,9 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@GeneratedValue @GeneratedValue
private UUID uuid; private UUID uuid;
@Column(name = "debitornumberprefix", columnDefinition = "numeric(5) not null")
private Integer debitorNumberPrefix;
@ManyToOne @ManyToOne
@JoinColumn(name = "personuuid", nullable = false) @JoinColumn(name = "personuuid", nullable = false)
private HsOfficePersonEntity person; private HsOfficePersonEntity person;
@ -44,7 +48,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private HsOfficeContactEntity contact; private HsOfficeContactEntity contact;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true) @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true)
@JoinColumn(name = "detailsuuid", nullable = true) @JoinColumn(name = "detailsuuid")
@NotFound(action = NotFoundAction.IGNORE) @NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details; private HsOfficePartnerDetailsEntity details;
@ -55,6 +59,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@Override @Override
public String toShortString() { public String toShortString() {
return person.toShortString(); return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("<person=null>");
} }
} }

View File

@ -6,7 +6,7 @@ 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.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.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;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;

View File

@ -12,8 +12,13 @@ components:
debitorNumber: debitorNumber:
type: integer type: integer
format: int32 format: int32
minimum: 10000 minimum: 1000000
maximum: 99999 maximum: 9999999
debitorNumberSuffix:
type: integer
format: int8
minimum: 00
maximum: 99
partner: partner:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner' $ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
billingContact: billingContact:
@ -27,6 +32,9 @@ components:
type: boolean type: boolean
refundBankAccount: refundBankAccount:
$ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount' $ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount'
defaultPrefix:
type: string
pattern: '^[a-z0-9]{3}$'
HsOfficeDebitorPatch: HsOfficeDebitorPatch:
type: object type: object
@ -49,6 +57,10 @@ components:
type: string type: string
format: uuid format: uuid
nullable: true nullable: true
defaultPrefix:
type: string
pattern: '^[a-z0-9]{3}$'
nullable: true
HsOfficeDebitorInsert: HsOfficeDebitorInsert:
type: object type: object
@ -61,11 +73,11 @@ components:
type: string type: string
format: uuid format: uuid
nullable: false nullable: false
debitorNumber: debitorNumberSuffix:
type: integer type: integer
format: int32 format: int8
minimum: 10000 minimum: 00
maximum: 99999 maximum: 99
vatId: vatId:
type: string type: string
vatCountryCode: vatCountryCode:
@ -76,6 +88,11 @@ components:
refundBankAccountUuid: refundBankAccountUuid:
type: string type: string
format: uuid format: uuid
defaultPrefix:
type: string
pattern: '^[a-z]{3}$'
required: required:
- partnerUuid - partnerUuid
- billingContactUuid - billingContactUuid
- defaultPrefix

View File

@ -155,7 +155,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
declare declare
cleanIdentifier varchar; cleanIdentifier varchar;
begin begin
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._]+', '', 'g'); cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._:]+', '', 'g');
return cleanIdentifier; return cleanIdentifier;
end; $$; end; $$;

View File

@ -28,8 +28,8 @@ create or replace function determineCurrentSubjectsUuids(currentUserUuid uuid, a
stable leakproof stable leakproof
language plpgsql as $$ language plpgsql as $$
declare declare
roleName varchar(63); roleName text;
roleNameParts varchar(63); roleNameParts text;
objectTableToAssume varchar(63); objectTableToAssume varchar(63);
objectNameToAssume varchar(63); objectNameToAssume varchar(63);
objectUuidToAssume uuid; objectUuidToAssume uuid;

View File

@ -31,6 +31,7 @@ call create_journal('hs_office_partner_details');
create table hs_office_partner create table hs_office_partner
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
debitorNumberPrefix varchar(5),
personUuid uuid not null references hs_office_person(uuid), personUuid uuid not null references hs_office_person(uuid),
contactUuid uuid not null references hs_office_contact(uuid), contactUuid uuid not null references hs_office_contact(uuid),
detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade detailsUuid uuid not null references hs_office_partner_details(uuid) on delete cascade

View File

@ -27,7 +27,6 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
hsOfficePartnerTenant RbacRoleDescriptor;
oldPerson hs_office_person; oldPerson hs_office_person;
newPerson hs_office_person; newPerson hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
@ -166,6 +165,8 @@ execute procedure hsOfficePartnerRbacRolesTrigger();
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityView('hs_office_partner', $idName$ call generateRbacIdentityView('hs_office_partner', $idName$
-- TODO: simplify by using just debitorNumberPrefix for the essential part
debitorNumberPrefix || ':' ||
(select idName from hs_office_person_iv p where p.uuid = target.personuuid) (select idName from hs_office_person_iv p where p.uuid = target.personuuid)
|| '-' || || '-' ||
(select idName from hs_office_contact_iv c where c.uuid = target.contactuuid) (select idName from hs_office_contact_iv c where c.uuid = target.contactuuid)

View File

@ -8,7 +8,10 @@
/* /*
Creates a single partner test record. Creates a single partner test record.
*/ */
create or replace procedure createHsOfficePartnerTestData( personTradeOrFamilyName varchar, contactLabel varchar ) create or replace procedure createHsOfficePartnerTestData(
debitorNumberPrefix numeric(5),
personTradeOrFamilyName varchar,
contactLabel varchar )
language plpgsql as $$ language plpgsql as $$
declare declare
currentTask varchar; currentTask varchar;
@ -51,8 +54,8 @@ begin
end if; end if;
insert insert
into hs_office_partner (uuid, personuuid, contactuuid, detailsUuid) into hs_office_partner (uuid, debitorNumberPrefix, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid); values (uuid_generate_v4(), debitorNumberPrefix, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
end; $$; end; $$;
--// --//
@ -64,11 +67,11 @@ end; $$;
do language plpgsql $$ do language plpgsql $$
begin begin
call createHsOfficePartnerTestData('First GmbH', 'first contact'); call createHsOfficePartnerTestData(10001, 'First GmbH', 'first contact');
call createHsOfficePartnerTestData('Second e.K.', 'second contact'); call createHsOfficePartnerTestData(10002, 'Second e.K.', 'second contact');
call createHsOfficePartnerTestData('Third OHG', 'third contact'); call createHsOfficePartnerTestData(10003, 'Third OHG', 'third contact');
call createHsOfficePartnerTestData('Fourth e.G.', 'forth contact'); call createHsOfficePartnerTestData(10004, 'Fourth e.G.', 'forth contact');
call createHsOfficePartnerTestData('Smith', 'fifth contact'); call createHsOfficePartnerTestData(10010, 'Smith', 'fifth contact');
end; end;
$$; $$;
--// --//

View File

@ -8,12 +8,17 @@ 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), partnerUuid uuid not null references hs_office_partner(uuid),
debitorNumber numeric(5) not null, billable boolean not null default true,
debitorNumberSuffix numeric(2) not null,
billingContactUuid uuid not null references hs_office_contact(uuid), billingContactUuid uuid not null references hs_office_contact(uuid),
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, -- TODO.spec: more of such? vatBusiness boolean not null, -- TODO.spec: more of such?
refundBankAccountUuid uuid references hs_office_bankaccount(uuid) refundBankAccountUuid uuid references hs_office_bankaccount(uuid),
defaultPrefix char(3) not null
constraint check_member_code check (
defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$'
)
-- TODO.impl: SEPA-mandate -- TODO.impl: SEPA-mandate
); );
--// --//

View File

@ -172,8 +172,10 @@ execute procedure hsOfficeDebitorRbacRolesTrigger();
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityView('hs_office_debitor', $idName$ call generateRbacIdentityView('hs_office_debitor', $idName$
'#' || debitorNumber || ':' || '#' ||
(select idName from hs_office_partner_iv p where p.uuid = target.partnerUuid) (select debitornumberprefix from hs_office_partner p where p.uuid = target.partnerUuid) ||
to_char(debitorNumberSuffix, 'fm00') ||
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv pi where pi.uuid = target.partnerUuid)
$idName$); $idName$);
--// --//
@ -181,14 +183,17 @@ call generateRbacIdentityView('hs_office_debitor', $idName$
-- ============================================================================ -- ============================================================================
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--// --changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumber', call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix',
$updates$ $updates$
partnerUuid = new.partnerUuid, partnerUuid = new.partnerUuid,
billable = new.billable,
billingContactUuid = new.billingContactUuid, billingContactUuid = new.billingContactUuid,
debitorNumberSuffix = new.debitorNumberSuffix,
refundBankAccountUuid = new.refundBankAccountUuid, refundBankAccountUuid = new.refundBankAccountUuid,
vatId = new.vatId, vatId = new.vatId,
vatCountryCode = new.vatCountryCode, vatCountryCode = new.vatCountryCode,
vatBusiness = new.vatBusiness vatBusiness = new.vatBusiness,
defaultPrefix = new.defaultPrefix
$updates$); $updates$);
--// --//

View File

@ -8,7 +8,12 @@
/* /*
Creates a single debitor test record. Creates a single debitor test record.
*/ */
create or replace procedure createHsOfficeDebitorTestData( partnerTradeName varchar, billingContactLabel varchar ) create or replace procedure createHsOfficeDebitorTestData(
debitorNumberSuffix numeric(5),
partnerTradeName varchar,
billingContactLabel varchar,
defaultPrefix varchar
)
language plpgsql as $$ language plpgsql as $$
declare declare
currentTask varchar; currentTask varchar;
@ -16,7 +21,6 @@ declare
relatedPartner hs_office_partner; relatedPartner hs_office_partner;
relatedContact hs_office_contact; relatedContact hs_office_contact;
relatedBankAccountUuid uuid; relatedBankAccountUuid uuid;
newDebitorNumber numeric(6);
begin begin
idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel); idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel);
currentTask := 'creating debitor test-data ' || idName; currentTask := 'creating debitor test-data ' || idName;
@ -28,14 +32,14 @@ begin
where person.tradeName = partnerTradeName into relatedPartner; where person.tradeName = partnerTradeName into relatedPartner;
select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact; select c.* from hs_office_contact c where c.label = billingContactLabel into relatedContact;
select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid; select b.uuid from hs_office_bankaccount b where b.holder = partnerTradeName into relatedBankAccountUuid;
select coalesce(max(debitorNumber)+1, 10001) from hs_office_debitor into newDebitorNumber;
raise notice 'creating test debitor: % (#%)', idName, newDebitorNumber; -- raise notice 'creating test debitor: % (#%)', idName, relatedPartner.debitorNumberPrefix || '00';
raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner; raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact; raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;
insert insert
into hs_office_debitor (uuid, partneruuid, debitornumber, billingcontactuuid, vatbusiness, refundbankaccountuuid) into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, refundbankaccountuuid, defaultprefix)
values (uuid_generate_v4(), relatedPartner.uuid, newDebitorNumber, relatedContact.uuid, true, relatedBankAccountUuid); values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, relatedBankAccountUuid, defaultPrefix);
end; $$; end; $$;
--// --//
@ -46,9 +50,9 @@ end; $$;
do language plpgsql $$ do language plpgsql $$
begin begin
call createHsOfficeDebitorTestData('First GmbH', 'first contact'); call createHsOfficeDebitorTestData(11, 'First GmbH', 'first contact', 'fir');
call createHsOfficeDebitorTestData('Second e.K.', 'second contact'); call createHsOfficeDebitorTestData(12, 'Second e.K.', 'second contact', 'sec');
call createHsOfficeDebitorTestData('Third OHG', 'third contact'); call createHsOfficeDebitorTestData(13, 'Third OHG', 'third contact', 'thi');
end; end;
$$; $$;
--// --//

View File

@ -15,7 +15,8 @@ create table if not exists hs_office_membership
mainDebitorUuid uuid not null references hs_office_debitor(uuid), mainDebitorUuid uuid not null references hs_office_debitor(uuid),
memberNumber numeric(5) not null unique, memberNumber numeric(5) not null unique,
validity daterange not null, validity daterange not null,
reasonForTermination HsOfficeReasonForTermination not null default 'NONE' reasonForTermination HsOfficeReasonForTermination not null default 'NONE',
membership_fee_billable boolean not null default true
); );
--// --//

View File

@ -92,7 +92,8 @@ execute procedure hsOfficeMembershipRbacRolesTrigger();
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--// --changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$ call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$
target.memberNumber || (select idName from hs_office_partner_iv p where p.uuid = target.partnerUuid) target.memberNumber ||
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid)
$idName$); $idName$);
--// --//
@ -104,7 +105,8 @@ call generateRbacRestrictedView('hs_office_membership',
orderby => 'target.memberNumber', orderby => 'target.memberNumber',
columnUpdates => $updates$ columnUpdates => $updates$
validity = new.validity, validity = new.validity,
reasonForTermination = new.reasonForTermination reasonForTermination = new.reasonForTermination,
membership_fee_billable = new.membership_fee_billable
$updates$); $updates$);
--// --//

View File

@ -8,11 +8,14 @@
/* /*
Creates a single membership test record. Creates a single membership test record.
*/ */
-- create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber integer )
create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric ) create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric )
language plpgsql as $$ language plpgsql as $$
declare declare
currentTask varchar; currentTask varchar;
idName varchar; idName varchar;
-- forDebitorNumberPrefix integer;
hsh-michaelhoennig marked this conversation as resolved

remove

remove

fixed

fixed
-- forDebitorNumberSuffix integer;
relatedPartner hs_office_partner; relatedPartner hs_office_partner;
relatedDebitor hs_office_debitor; relatedDebitor hs_office_debitor;
newMemberNumber numeric; newMemberNumber numeric;
@ -25,7 +28,10 @@ begin
select partner.* from hs_office_partner partner select partner.* from hs_office_partner partner
join hs_office_person person on person.uuid = partner.personUuid join hs_office_person person on person.uuid = partner.personUuid
where person.tradeName = forPartnerTradeName into relatedPartner; where person.tradeName = forPartnerTradeName into relatedPartner;
select d.* from hs_office_debitor d where d.debitorNumber = forMainDebitorNumber into relatedDebitor; -- forDebitorNumberPrefix := forMainDebitorNumber/ 100;
-- forDebitorNumberSuffix := mod(forMainDebitorNumber, 100);
-- select d.* from hs_office_debitor d where d.debitorNumberSuffix = forDebitorNumberSuffix into relatedDebitor;
select d.* from hs_office_debitor d where d.debitorNumberSuffix = forMainDebitorNumber into relatedDebitor;
select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber; select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber;
raise notice 'creating test Membership: %', idName; raise notice 'creating test Membership: %', idName;
@ -33,6 +39,7 @@ begin
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert insert
into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination) into hs_office_membership (uuid, partneruuid, maindebitoruuid, membernumber, validity, reasonfortermination)
-- values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, forDebitorNumberPrefix, daterange('20221001' , null, '[]'), 'NONE');
values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE'); values (uuid_generate_v4(), relatedPartner.uuid, relatedDebitor.uuid, newMemberNumber, daterange('20221001' , null, '[]'), 'NONE');
end; $$; end; $$;
--// --//
@ -44,9 +51,14 @@ end; $$;
do language plpgsql $$ do language plpgsql $$
begin begin
call createHsOfficeMembershipTestData('First GmbH', 10001); call createHsOfficeMembershipTestData('First GmbH', 11);
call createHsOfficeMembershipTestData('Second e.K.', 10002); call createHsOfficeMembershipTestData('Second e.K.', 12);
call createHsOfficeMembershipTestData('Third OHG', 10003); call createHsOfficeMembershipTestData('Third OHG', 13);
end; end;
-- begin
-- call createHsOfficeMembershipTestData('First GmbH', 1000100);
-- call createHsOfficeMembershipTestData('Second e.K.', 1000200);
-- call createHsOfficeMembershipTestData('Third OHG', 1000300);
-- end;
$$; $$;
--// --//

View File

@ -200,7 +200,9 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeCoopSharesPackageRule = classes() public static final ArchRule hsOfficeCoopSharesPackageRule = classes()
.that().resideInAPackage("..hs.office.coopshares..") .that().resideInAPackage("..hs.office.coopshares..")
.should().onlyBeAccessed().byClassesThat() .should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.coopshares.."); .resideInAnyPackage(
"..hs.office.coopshares..",
"..hs.office.migration..");
@ArchTest @ArchTest
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -117,7 +117,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm view on coopassetstransaction#temprefB to role membership#10001....tenant by system and assume }", "{ grant perm view on coopassetstransaction#temprefB to role membership#10001:....tenant by system and assume }",
null)); null));
} }
@ -200,8 +200,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
@Test @Test
public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() { public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() {
// given: // given:
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.admin",
// when: // when:
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(

View File

@ -116,7 +116,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
"{ grant perm view on coopsharestransaction#temprefB to role membership#10001....tenant by system and assume }", "{ grant perm view on coopsharestransaction#temprefB to role membership#10001:....tenant by system and assume }",
null)); null));
} }
@ -199,8 +199,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Test @Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() { public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given: // given:
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.admin",
// when: // when:
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(

View File

@ -35,8 +35,8 @@ import static org.hamcrest.Matchers.*;
@Transactional @Transactional
class HsOfficeDebitorControllerAcceptanceTest { class HsOfficeDebitorControllerAcceptanceTest {
private static final int LOWEST_TEMP_DEBITOR_NUMBER = 20000; private static final int LOWEST_TEMP_DEBITOR_SUFFIX = 90;
private static int nextDebitorNumber = LOWEST_TEMP_DEBITOR_NUMBER; private static byte nextDebitorSuffix = LOWEST_TEMP_DEBITOR_SUFFIX;
@LocalServerPort @LocalServerPort
private Integer port; private Integer port;
@ -81,7 +81,8 @@ class HsOfficeDebitorControllerAcceptanceTest {
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
[ [
{ {
"debitorNumber": 10001, "debitorNumber": 1000111,
"debitorNumberSuffix": 11,
"partner": { "person": { "personType": "LEGAL" } }, "partner": { "person": { "personType": "LEGAL" } },
"billingContact": { "label": "first contact" }, "billingContact": { "label": "first contact" },
"vatId": null, "vatId": null,
@ -90,7 +91,8 @@ class HsOfficeDebitorControllerAcceptanceTest {
"refundBankAccount": { "holder": "First GmbH" } "refundBankAccount": { "holder": "First GmbH" }
}, },
{ {
"debitorNumber": 10002, "debitorNumber": 1000212,
"debitorNumberSuffix": 12,
"partner": { "person": { "tradeName": "Second e.K." } }, "partner": { "person": { "tradeName": "Second e.K." } },
"billingContact": { "label": "second contact" }, "billingContact": { "label": "second contact" },
"vatId": null, "vatId": null,
@ -99,7 +101,8 @@ class HsOfficeDebitorControllerAcceptanceTest {
"refundBankAccount": { "holder": "Second e.K." } "refundBankAccount": { "holder": "Second e.K." }
}, },
{ {
"debitorNumber": 10003, "debitorNumber": 1000313,
"debitorNumberSuffix": 13,
"partner": { "person": { "tradeName": "Third OHG" } }, "partner": { "person": { "tradeName": "Third OHG" } },
"billingContact": { "label": "third contact" }, "billingContact": { "label": "third contact" },
"vatId": null, "vatId": null,
@ -120,14 +123,14 @@ class HsOfficeDebitorControllerAcceptanceTest {
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/debitors?debitorNumber=10002") .get("http://localhost/api/hs/office/debitors?debitorNumber=1000212")
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
[ [
{ {
"debitorNumber": 10002, "debitorNumber": 1000212,
"partner": { "person": { "tradeName": "Second e.K." } }, "partner": { "person": { "tradeName": "Second e.K." } },
"billingContact": { "label": "second contact" }, "billingContact": { "label": "second contact" },
"vatId": null, "vatId": null,
@ -160,13 +163,14 @@ class HsOfficeDebitorControllerAcceptanceTest {
{ {
"partnerUuid": "%s", "partnerUuid": "%s",
"billingContactUuid": "%s", "billingContactUuid": "%s",
"debitorNumber": "%s", "debitorNumberSuffix": "%s",
"vatId": "VAT123456", "vatId": "VAT123456",
"vatCountryCode": "DE", "vatCountryCode": "DE",
"vatBusiness": true, "vatBusiness": true,
"refundBankAccountUuid": "%s" "refundBankAccountUuid": "%s",
"defaultPrefix": "for"
} }
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber, givenBankAccount.getUuid())) """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix, givenBankAccount.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
@ -175,6 +179,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("vatId", is("VAT123456")) .body("vatId", is("VAT123456"))
.body("defaultPrefix", is("for"))
.body("billingContact.label", is(givenContact.getLabel())) .body("billingContact.label", is(givenContact.getLabel()))
.body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName())) .body("partner.person.tradeName", is(givenPartner.getPerson().getTradeName()))
.body("refundBankAccount.holder", is(givenBankAccount.getHolder())) .body("refundBankAccount.holder", is(givenBankAccount.getHolder()))
@ -202,9 +207,10 @@ class HsOfficeDebitorControllerAcceptanceTest {
{ {
"partnerUuid": "%s", "partnerUuid": "%s",
"billingContactUuid": "%s", "billingContactUuid": "%s",
"debitorNumber": "%s" "debitorNumberSuffix": "%s",
"defaultPrefix": "for"
} }
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber)) """.formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
@ -218,6 +224,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
.body("vatCountryCode", equalTo(null)) .body("vatCountryCode", equalTo(null))
.body("vatBusiness", equalTo(false)) .body("vatBusiness", equalTo(false))
.body("refundBankAccount", equalTo(null)) .body("refundBankAccount", equalTo(null))
.body("defaultPrefix", equalTo("for"))
.header("Location", startsWith("http://localhost")) .header("Location", startsWith("http://localhost"))
.extract().header("Location"); // @formatter:on .extract().header("Location"); // @formatter:on
@ -242,12 +249,14 @@ class HsOfficeDebitorControllerAcceptanceTest {
{ {
"partnerUuid": "%s", "partnerUuid": "%s",
"billingContactUuid": "%s", "billingContactUuid": "%s",
"debitorNumber": "%s", "debitorNumberSuffix": "%s",
"vatId": "VAT123456", "vatId": "VAT123456",
"vatCountryCode": "DE", "vatCountryCode": "DE",
"vatBusiness": true "vatBusiness": true,
"defaultPrefix": "thi"
} }
""".formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorNumber)) """
.formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorSuffix))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
@ -272,12 +281,13 @@ class HsOfficeDebitorControllerAcceptanceTest {
{ {
"partnerUuid": "%s", "partnerUuid": "%s",
"billingContactUuid": "%s", "billingContactUuid": "%s",
"debitorNumber": "%s", "debitorNumberSuffix": "%s",
"vatId": "VAT123456", "vatId": "VAT123456",
"vatCountryCode": "DE", "vatCountryCode": "DE",
"vatBusiness": true "vatBusiness": true,
"defaultPrefix": "for"
} }
""".formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorNumber)) """.formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/debitors") .post("http://localhost/api/hs/office/debitors")
@ -375,7 +385,8 @@ class HsOfficeDebitorControllerAcceptanceTest {
"contactUuid": "%s", "contactUuid": "%s",
"vatId": "VAT222222", "vatId": "VAT222222",
"vatCountryCode": "AA", "vatCountryCode": "AA",
"vatBusiness": true "vatBusiness": true,
"defaultPrefix": "for"
} }
""".formatted(givenContact.getUuid())) """.formatted(givenContact.getUuid()))
.port(port) .port(port)
@ -388,6 +399,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
.body("vatId", is("VAT222222")) .body("vatId", is("VAT222222"))
.body("vatCountryCode", is("AA")) .body("vatCountryCode", is("AA"))
.body("vatBusiness", is(true)) .body("vatBusiness", is(true))
.body("defaultPrefix", is("for"))
.body("billingContact.label", is(givenContact.getLabel())) .body("billingContact.label", is(givenContact.getLabel()))
.body("partner.person.tradeName", is(givenDebitor.getPartner().getPerson().getTradeName())); .body("partner.person.tradeName", is(givenDebitor.getPartner().getPerson().getTradeName()));
// @formatter:on // @formatter:on
@ -522,9 +534,10 @@ class HsOfficeDebitorControllerAcceptanceTest {
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0);
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumber(++nextDebitorNumber) .debitorNumberSuffix(++nextDebitorSuffix)
.partner(givenPartner) .partner(givenPartner)
.billingContact(givenContact) .billingContact(givenContact)
.defaultPrefix("abc")
.build(); .build();
return debitorRepo.save(newDebitor); return debitorRepo.save(newDebitor);
@ -537,7 +550,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
jpaAttempt.transacted(() -> { jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var count = em.createQuery( final var count = em.createQuery(
"DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumber > " + LOWEST_TEMP_DEBITOR_NUMBER) "DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumberSuffix >= " + LOWEST_TEMP_DEBITOR_SUFFIX)
.executeUpdate(); .executeUpdate();
System.out.printf("deleted %d entities%n", count); System.out.printf("deleted %d entities%n", count);
}); });

View File

@ -31,6 +31,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID(); private static final UUID INITIAL_CONTACT_UUID = UUID.randomUUID();
private static final UUID PATCHED_CONTACT_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_VAT_COUNTRY_CODE = "ZZ"; private static final String PATCHED_VAT_COUNTRY_CODE = "ZZ";
private static final boolean PATCHED_VAT_BUSINESS = false; private static final boolean PATCHED_VAT_BUSINESS = false;
@ -62,6 +63,7 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
entity.setVatId("initial VAT-ID"); entity.setVatId("initial VAT-ID");
entity.setVatCountryCode("AA"); entity.setVatCountryCode("AA");
entity.setVatBusiness(true); entity.setVatBusiness(true);
entity.setDefaultPrefix("abc");
return entity; return entity;
} }
@ -100,6 +102,12 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
HsOfficeDebitorPatchResource::setVatBusiness, HsOfficeDebitorPatchResource::setVatBusiness,
PATCHED_VAT_BUSINESS, PATCHED_VAT_BUSINESS,
HsOfficeDebitorEntity::setVatBusiness) HsOfficeDebitorEntity::setVatBusiness)
.notNullable(),
new JsonNullableProperty<>(
"defaultPrefix",
HsOfficeDebitorPatchResource::setDefaultPrefix,
PATCHED_DEFAULT_PREFIX,
HsOfficeDebitorEntity::setDefaultPrefix)
.notNullable() .notNullable()
); );
} }

View File

@ -13,30 +13,50 @@ class HsOfficeDebitorEntityUnitTest {
@Test @Test
void toStringContainsPartnerAndContact() { void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorNumber(123456) .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder() .partner(HsOfficePartnerEntity.builder()
.person(HsOfficePersonEntity.builder() .person(HsOfficePersonEntity.builder()
.tradeName("some trade name") .tradeName("some trade name")
.build()) .build())
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build()) .details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
.debitorNumberPrefix(12345)
.build()) .build())
.billingContact(HsOfficeContactEntity.builder().label("some label").build()) .billingContact(HsOfficeContactEntity.builder().label("some label").build())
.build(); .build();
final var result = given.toString(); final var result = given.toString();
assertThat(result).isEqualTo("debitor(123456: some trade name)"); assertThat(result).isEqualTo("debitor(1234567: some trade name)");
} }
@Test @Test
void toShortStringContainsPartnerAndContact() { void toStringWithoutPersonContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder() final var given = HsOfficeDebitorEntity.builder()
.debitorNumber(123456) .debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
.person(null)
.details(HsOfficePartnerDetailsEntity.builder().birthName("some birth name").build())
.debitorNumberPrefix(12345)
.build())
.billingContact(HsOfficeContactEntity.builder().label("some label").build())
.build();
final var result = given.toString();
assertThat(result).isEqualTo("debitor(1234567: <person=null>)");
}
@Test
void toShortStringContainsDebitorNumber() {
final var given = HsOfficeDebitorEntity.builder()
.partner(HsOfficePartnerEntity.builder()
.debitorNumberPrefix(12345)
.build())
.debitorNumberSuffix((byte)67)
.build(); .build();
final var result = given.toShortString(); final var result = given.toShortString();
assertThat(result).isEqualTo("123456"); assertThat(result).isEqualTo("1234567");
} }
} }

View File

@ -81,9 +81,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumber(20001) .debitorNumberSuffix((byte)21)
.partner(givenPartner) .partner(givenPartner)
.billingContact(givenContact) .billingContact(givenContact)
.defaultPrefix("abc")
.build(); .build();
return debitorRepo.save(newDebitor); return debitorRepo.save(newDebitor);
}); });
@ -95,14 +96,43 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
assertThat(debitorRepo.count()).isEqualTo(count + 1); assertThat(debitorRepo.count()).isEqualTo(count + 1);
} }
// @ParameterizedTest
// @ValueSource(strings = {"", "a", "ab", "a12", "123", "12a"})
// public void canNotCreateNewDebitorWithInvalidDefaultPrefix(final String givenPrefix) {
// // given
// context("superuser-alex@hostsharing.net");
// final var count = debitorRepo.count();
// final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("First GmbH").get(0);
// final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
//
// // when
// final var result = attempt(em, () -> {
// final var newDebitor = HsOfficeDebitorEntity.builder()
// .debitorNumberSuffix((byte)21)
// .partner(givenPartner)
// .billingContact(givenContact)
// .defaultPrefix(givenPrefix)
// .build();
// return debitorRepo.save(newDebitor);
// });
//
// // then
// result.assertSuccessful();
// assertThat(result.returnedValue()).isNotNull().extracting(HsOfficeDebitorEntity::getUuid).isNotNull();
// assertThatDebitorIsPersisted(result.returnedValue());
// assertThat(debitorRepo.count()).isEqualTo(count + 1);
// }
@Test @Test
public void createsAndGrantsRoles() { public void createsAndGrantsRoles() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream() final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream()
// some search+replace to make the output fit into the screen width
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
.map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG")) // .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("forthcontact", "4th"))
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
@ -113,9 +143,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0); final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0);
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumber(20002) .debitorNumberSuffix((byte)22)
.partner(givenPartner) .partner(givenPartner)
.billingContact(givenContact) .billingContact(givenContact)
.defaultPrefix("abc")
.build(); .build();
return debitorRepo.save(newDebitor); return debitorRepo.save(newDebitor);
}).assertSuccessful(); }).assertSuccessful();
@ -123,42 +154,43 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then // then
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_debitor#20002Fourthe.G.-forthcontact.owner", "hs_office_debitor#1000422:Fourthe.G.-forthcontact.owner",
"hs_office_debitor#20002Fourthe.G.-forthcontact.admin", "hs_office_debitor#1000422:Fourthe.G.-forthcontact.admin",
"hs_office_debitor#20002Fourthe.G.-forthcontact.agent", "hs_office_debitor#1000422:Fourthe.G.-forthcontact.agent",
"hs_office_debitor#20002Fourthe.G.-forthcontact.tenant", "hs_office_debitor#1000422:Fourthe.G.-forthcontact.tenant",
"hs_office_debitor#20002Fourthe.G.-forthcontact.guest")); "hs_office_debitor#1000422:Fourthe.G.-forthcontact.guest"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())) assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex")) .map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
.map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG")) // .map(s -> s.replace("1000422Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("22Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("Fourthe.G.-forthcontact", "FeG")) .map(s -> s.replace("Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("forthcontact", "4th"))
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// owner // owner
"{ grant perm * on debitor#FeG to role debitor#FeG.owner by system and assume }", "{ grant perm * on debitor#1000422:FeG to role debitor#1000422:FeG.owner by system and assume }",
"{ grant role debitor#FeG.owner to role global#global.admin by system and assume }", "{ grant role debitor#1000422:FeG.owner to role global#global.admin by system and assume }",
"{ grant role debitor#FeG.owner to user superuser-alex by global#global.admin and assume }", "{ grant role debitor#1000422:FeG.owner to user superuser-alex by global#global.admin and assume }",
// admin // admin
"{ grant perm edit on debitor#FeG to role debitor#FeG.admin by system and assume }", "{ grant perm edit on debitor#1000422:FeG to role debitor#1000422:FeG.admin by system and assume }",
"{ grant role debitor#FeG.admin to role debitor#FeG.owner by system and assume }", "{ grant role debitor#1000422:FeG.admin to role debitor#1000422:FeG.owner by system and assume }",
// agent // agent
"{ grant role debitor#FeG.agent to role debitor#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#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#FeG.agent to role partner#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#FeG.tenant by system and assume }", "{ grant role contact#4th.guest to role debitor#1000422:FeG.tenant by system and assume }",
"{ grant role debitor#FeG.tenant to role debitor#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#FeG.tenant to role partner#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#FeG.tenant to role debitor#FeG.tenant by system and assume }", "{ grant role partner#10004:FeG.tenant to role debitor#1000422:FeG.tenant by system and assume }",
// guest // guest
"{ grant perm view on debitor#FeG to role debitor#FeG.guest by system and assume }", "{ grant perm view on debitor#1000422:FeG to role debitor#1000422:FeG.guest by system and assume }",
"{ grant role debitor#FeG.guest to role debitor#FeG.tenant by system and assume }", "{ grant role debitor#1000422:FeG.guest to role debitor#1000422:FeG.tenant by system and assume }",
null)); null));
} }
@ -183,14 +215,14 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then // then
allTheseDebitorsAreReturned( allTheseDebitorsAreReturned(
result, result,
"debitor(10001: First GmbH)", "debitor(1000111: First GmbH)",
"debitor(10002: Second e.K.)", "debitor(1000212: Second e.K.)",
"debitor(10003: Third OHG)"); "debitor(1000313: Third OHG)");
} }
@ParameterizedTest @ParameterizedTest
@ValueSource(strings = { @ValueSource(strings = {
"hs_office_partner#FirstGmbH-firstcontact.admin", "hs_office_partner#10001:FirstGmbH-firstcontact.admin",
"hs_office_person#FirstGmbH.admin", "hs_office_person#FirstGmbH.admin",
"hs_office_contact#firstcontact.admin", "hs_office_contact#firstcontact.admin",
}) })
@ -202,7 +234,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = debitorRepo.findDebitorByOptionalNameLike(null); final var result = debitorRepo.findDebitorByOptionalNameLike(null);
// then: // then:
exactlyTheseDebitorsAreReturned(result, "debitor(10001: First GmbH)"); exactlyTheseDebitorsAreReturned(result, "debitor(1000111: First GmbH)", "debitor(1000120: First GmbH)");
} }
@Test @Test
@ -227,10 +259,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
// when // when
final var result = debitorRepo.findDebitorByDebitorNumber(10003); final var result = debitorRepo.findDebitorByDebitorNumber(1000313);
// then // then
exactlyTheseDebitorsAreReturned(result, "debitor(10003: Third OHG)"); // exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)", "debitor(1000413: Fourth e.G.)");
exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)");
} }
} }
@ -246,7 +279,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = debitorRepo.findDebitorByOptionalNameLike("third contact"); final var result = debitorRepo.findDebitorByOptionalNameLike("third contact");
// then // then
exactlyTheseDebitorsAreReturned(result, "debitor(10003: Third OHG)"); exactlyTheseDebitorsAreReturned(result, "debitor(1000313: Third OHG)");
} }
} }
@ -257,10 +290,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_canUpdateArbitraryDebitor() { public void globalAdmin_canUpdateArbitraryDebitor() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_partner#Fourthe.G.-forthcontact.admin"); "hs_office_partner#10004:Fourthe.G.-forthcontact.admin");
assertThatDebitorActuallyInDatabase(givenDebitor); assertThatDebitorActuallyInDatabase(givenDebitor);
final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0); final var givenNewPartner = partnerRepo.findPartnerByOptionalNameLike("First").get(0);
final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0); final var givenNewContact = contactRepo.findContactByOptionalLabelLike("sixth contact").get(0);
@ -290,10 +323,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// ... partner role was reassigned: // ... partner role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole( assertThatDebitorIsNotVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"hs_office_partner#Fourthe.G.-forthcontact.agent"); "hs_office_partner#10004:Fourthe.G.-forthcontact.agent");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
result.returnedValue(), result.returnedValue(),
"hs_office_partner#FirstGmbH-firstcontact.agent"); "hs_office_partner#10001:FirstGmbH-firstcontact.agent");
// ... contact role was reassigned: // ... contact role was reassigned:
assertThatDebitorIsNotVisibleForUserWithRole( assertThatDebitorIsNotVisibleForUserWithRole(
@ -316,10 +349,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_canUpdateNullRefundBankAccountToNotNullBankAccountForArbitraryDebitor() { public void globalAdmin_canUpdateNullRefundBankAccountToNotNullBankAccountForArbitraryDebitor() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", null, "fif");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_partner#Fourthe.G.-forthcontact.admin"); "hs_office_partner#10004:Fourthe.G.-forthcontact.admin");
assertThatDebitorActuallyInDatabase(givenDebitor); assertThatDebitorActuallyInDatabase(givenDebitor);
final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0); final var givenNewBankAccount = bankAccountRepo.findByOptionalHolderLike("first").get(0);
@ -346,10 +379,10 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_canUpdateRefundBankAccountToNullForArbitraryDebitor() { public void globalAdmin_canUpdateRefundBankAccountToNullForArbitraryDebitor() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "fifth contact", "Fourth", "fif");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_partner#Fourthe.G.-forthcontact.admin"); "hs_office_partner#10004:Fourthe.G.-forthcontact.admin");
assertThatDebitorActuallyInDatabase(givenDebitor); assertThatDebitorActuallyInDatabase(givenDebitor);
// when // when
@ -375,15 +408,15 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void partnerAdmin_canNotUpdateRelatedDebitor() { public void partnerAdmin_canNotUpdateRelatedDebitor() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eighth", "Fourth", "eig");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_partner#Fourthe.G.-forthcontact.admin"); "hs_office_partner#10004:Fourthe.G.-forthcontact.admin");
assertThatDebitorActuallyInDatabase(givenDebitor); assertThatDebitorActuallyInDatabase(givenDebitor);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_partner#Fourthe.G.-forthcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_partner#10004:Fourthe.G.-forthcontact.admin");
givenDebitor.setVatId("NEW-VAT-ID"); givenDebitor.setVatId("NEW-VAT-ID");
return debitorRepo.save(givenDebitor); return debitorRepo.save(givenDebitor);
}); });
@ -397,7 +430,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void contactAdmin_canNotUpdateRelatedDebitor() { public void contactAdmin_canNotUpdateRelatedDebitor() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "ninth", "Fourth", "nin");
assertThatDebitorIsVisibleForUserWithRole( assertThatDebitorIsVisibleForUserWithRole(
givenDebitor, givenDebitor,
"hs_office_contact#ninthcontact.admin"); "hs_office_contact#ninthcontact.admin");
@ -448,7 +481,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_canDeleteAnyDebitor() { public void globalAdmin_canDeleteAnyDebitor() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "tenth", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "tenth", "Fourth", "ten");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -468,7 +501,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void relatedPerson_canNotDeleteTheirRelatedDebitor() { public void relatedPerson_canNotDeleteTheirRelatedDebitor() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "eleventh", "Fourth", "ele");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -494,7 +527,7 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll()));
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth", "Fourth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth", "Fourth", "twe");
assertThat(rawRoleRepo.findAll().size()).as("precondition failed: unexpected number of roles created") assertThat(rawRoleRepo.findAll().size()).as("precondition failed: unexpected number of roles created")
.isEqualTo(initialRoleNames.length + 5); .isEqualTo(initialRoleNames.length + 5);
assertThat(rawGrantRepo.findAll().size()).as("precondition failed: unexpected number of grants created") assertThat(rawGrantRepo.findAll().size()).as("precondition failed: unexpected number of grants created")
@ -536,7 +569,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
private HsOfficeDebitorEntity givenSomeTemporaryDebitor( private HsOfficeDebitorEntity givenSomeTemporaryDebitor(
final String partner, final String partner,
final String contact, final String contact,
final String bankAccount) { final String bankAccount,
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 givenPartner = partnerRepo.findPartnerByOptionalNameLike(partner).get(0);
@ -544,10 +578,11 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var givenBankAccount = final var givenBankAccount =
bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null; bankAccount != null ? bankAccountRepo.findByOptionalHolderLike(bankAccount).get(0) : null;
final var newDebitor = HsOfficeDebitorEntity.builder() final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumber(20000) .debitorNumberSuffix((byte)20)
.partner(givenPartner) .partner(givenPartner)
.billingContact(givenContact) .billingContact(givenContact)
.refundBankAccount(givenBankAccount) .refundBankAccount(givenBankAccount)
.defaultPrefix(defaultPrefix)
.build(); .build();
return debitorRepo.save(newDebitor); return debitorRepo.save(newDebitor);
@ -558,7 +593,8 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
@AfterEach @AfterEach
void cleanup() { void cleanup() {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumber >= 20000").executeUpdate(); // TODO em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20").executeUpdate();
em.createQuery("DELETE FROM HsOfficeDebitorEntity d where d.debitorNumberSuffix >= 20000").executeUpdate();
} }
void exactlyTheseDebitorsAreReturned(final List<HsOfficeDebitorEntity> actualResult, final String... debitorNames) { void exactlyTheseDebitorsAreReturned(final List<HsOfficeDebitorEntity> actualResult, final String... debitorNames) {

View File

@ -9,8 +9,10 @@ import static net.hostsharing.hsadminng.hs.office.partner.TestHsOfficePartner.TE
@UtilityClass @UtilityClass
public class TestHsOfficeDebitor { public class TestHsOfficeDebitor {
public byte DEFAULT_DEBITOR_SUFFIX = 0;
public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder() public static final HsOfficeDebitorEntity TEST_DEBITOR = HsOfficeDebitorEntity.builder()
.debitorNumber(10001) .debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
.partner(TEST_PARTNER) .partner(TEST_PARTNER)
.billingContact(TEST_CONTACT) .billingContact(TEST_CONTACT)
.build(); .build();

View File

@ -83,7 +83,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
[ [
{ {
"partner": { "person": { "tradeName": "First GmbH" } }, "partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 10001 }, "mainDebitor": { "debitorNumber": 1000111 },
"memberNumber": 10001, "memberNumber": 10001,
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
@ -91,7 +91,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
}, },
{ {
"partner": { "person": { "tradeName": "Second e.K." } }, "partner": { "person": { "tradeName": "Second e.K." } },
"mainDebitor": { "debitorNumber": 10002 }, "mainDebitor": { "debitorNumber": 1000212 },
"memberNumber": 10002, "memberNumber": 10002,
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
@ -99,7 +99,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
}, },
{ {
"partner": { "person": { "tradeName": "Third OHG" } }, "partner": { "person": { "tradeName": "Third OHG" } },
"mainDebitor": { "debitorNumber": 10003 }, "mainDebitor": { "debitorNumber": 1000313 },
"memberNumber": 10003, "memberNumber": 10003,
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
@ -142,6 +142,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber())) .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumber()))
// .body("mainDebitor.debitorNumber", is(givenDebitor.getDebitorNumberSuffix()))
.body("partner.person.tradeName", is("Third OHG")) .body("partner.person.tradeName", is("Third OHG"))
.body("memberNumber", is(20001)) .body("memberNumber", is(20001))
.body("validFrom", is("2022-10-13")) .body("validFrom", is("2022-10-13"))
@ -182,7 +183,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
{ {
"partner": { "person": { "tradeName": "First GmbH" } }, "partner": { "person": { "tradeName": "First GmbH" } },
"mainDebitor": { "debitorNumber": 10001 }, "mainDebitor": { "debitorNumber": 1000111 },
"memberNumber": 10001, "memberNumber": 10001,
"validFrom": "2022-10-01", "validFrom": "2022-10-01",
"validTo": null, "validTo": null,
@ -224,7 +225,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.header("assumed-roles", "hs_office_debitor#10003ThirdOHG-thirdcontact.agent") .header("assumed-roles", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.agent")
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid) .get("http://localhost/api/hs/office/memberships/" + givenMembershipUuid)
@ -235,7 +236,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
{ {
"partner": { "person": { "tradeName": "Third OHG" } }, "partner": { "person": { "tradeName": "Third OHG" } },
"mainDebitor": { "mainDebitor": {
"debitorNumber": 10003, "debitorNumber": 1000313,
"billingContact": { "label": "third contact" } "billingContact": { "label": "third contact" }
}, },
"memberNumber": 10003, "memberNumber": 10003,
@ -276,6 +277,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
.body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber())) .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumber()))
// .body("mainDebitor.debitorNumber", is(givenMembership.getMainDebitor().getDebitorNumberSuffix()))
.body("memberNumber", is(givenMembership.getMemberNumber())) .body("memberNumber", is(givenMembership.getMemberNumber()))
.body("validFrom", is("2022-11-01")) .body("validFrom", is("2022-11-01"))
.body("validTo", is("2023-12-31")) .body("validTo", is("2023-12-31"))
@ -299,7 +301,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenMembership = givenSomeTemporaryMembershipBessler(); final var givenMembership = givenSomeTemporaryMembershipBessler();
final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(10003).get(0); final var givenNewMainDebitor = debitorRepo.findDebitorByDebitorNumber(1000313).get(0);
RestAssured // @formatter:off RestAssured // @formatter:off
.given() .given()
@ -318,7 +320,7 @@ class HsOfficeMembershipControllerAcceptanceTest {
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body("uuid", isUuidValid()) .body("uuid", isUuidValid())
.body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName())) .body("partner.person.tradeName", is(givenMembership.getPartner().getPerson().getTradeName()))
.body("mainDebitor.debitorNumber", is(10003)) .body("mainDebitor.debitorNumber", is(1000313))
.body("memberNumber", is(givenMembership.getMemberNumber())) .body("memberNumber", is(givenMembership.getMemberNumber()))
.body("validFrom", is("2022-11-01")) .body("validFrom", is("2022-11-01"))
.body("validTo", nullValue()) .body("validTo", nullValue())
@ -340,13 +342,13 @@ class HsOfficeMembershipControllerAcceptanceTest {
@Test @Test
void partnerAgent_canViewButNotPatchValidityOfRelatedMembership() { void partnerAgent_canViewButNotPatchValidityOfRelatedMembership() {
context.define("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.agent"); context.define("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.agent");
final var givenMembership = givenSomeTemporaryMembershipBessler(); final var givenMembership = givenSomeTemporaryMembershipBessler();
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
.header("current-user", "superuser-alex@hostsharing.net") .header("current-user", "superuser-alex@hostsharing.net")
.header("assumed-roles", "hs_office_partner#FirstGmbH-firstcontact.agent") .header("assumed-roles", "hs_office_partner#10001:FirstGmbH-firstcontact.agent")
.contentType(ContentType.JSON) .contentType(ContentType.JSON)
.body(""" .body("""
{ {

View File

@ -27,7 +27,8 @@ class HsOfficeMembershipEntityUnitTest {
void toStringContainsAllProps() { void toStringContainsAllProps() {
final var result = givenMembership.toString(); final var result = givenMembership.toString();
assertThat(result).isEqualTo("Membership(10001, Test Ltd., 10001, [2020-01-01,))"); assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))");
// assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))");
} }
@Test @Test

View File

@ -119,11 +119,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var all = rawRoleRepo.findAll(); final var all = rawRoleRepo.findAll();
assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_membership#20002FirstGmbH-firstcontact.admin", "hs_office_membership#20002:FirstGmbH-firstcontact.admin",
"hs_office_membership#20002FirstGmbH-firstcontact.agent", "hs_office_membership#20002:FirstGmbH-firstcontact.agent",
"hs_office_membership#20002FirstGmbH-firstcontact.guest", "hs_office_membership#20002:FirstGmbH-firstcontact.guest",
"hs_office_membership#20002FirstGmbH-firstcontact.owner", "hs_office_membership#20002:FirstGmbH-firstcontact.owner",
"hs_office_membership#20002FirstGmbH-firstcontact.tenant")); "hs_office_membership#20002:FirstGmbH-firstcontact.tenant"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())) assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("GmbH-firstcontact", "")) .map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", "")) .map(s -> s.replace("hs_office_", ""))
@ -131,32 +131,36 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
initialGrantNames, initialGrantNames,
// owner // owner
"{ grant perm * on membership#20002First to role membership#20002First.owner by system and assume }", "{ grant perm * on membership#20002:First to role membership#20002:First.owner by system and assume }",
"{ grant role membership#20002First.owner to role global#global.admin by system and assume }", "{ grant role membership#20002:First.owner to role global#global.admin by system and assume }",
// admin // admin
"{ grant perm edit on membership#20002First to role membership#20002First.admin by system and assume }", "{ grant perm edit on membership#20002:First to role membership#20002:First.admin by system and assume }",
"{ grant role membership#20002First.admin to role membership#20002First.owner by system and assume }", "{ grant role membership#20002:First.admin to role membership#20002:First.owner by system and assume }",
// agent // agent
"{ grant role membership#20002First.agent to role membership#20002First.admin by system and assume }", "{ grant role membership#20002:First.agent to role membership#20002:First.admin by system and assume }",
"{ grant role partner#First.tenant to role membership#20002First.agent by system and assume }", "{ grant role partner#10001:First.tenant to role membership#20002:First.agent by system and assume }",
"{ grant role membership#20002First.agent to role debitor#10001First.admin by system and assume }", "{ grant role membership#20002:First.agent to role debitor#1000111:First.admin by system and assume }",
"{ grant role membership#20002First.agent to role partner#First.admin by system and assume }", "{ grant role membership#20002:First.agent to role partner#10001:First.admin by system and assume }",
"{ grant role debitor#10001First.tenant to role membership#20002First.agent by system and assume }", "{ grant role debitor#1000111:First.tenant to role membership#20002:First.agent by system and assume }",
// tenant // tenant
"{ grant role membership#20002First.tenant to role membership#20002First.agent by system and assume }", "{ grant role membership#20002:First.tenant to role membership#20002:First.agent by system and assume }",
"{ grant role partner#First.guest to role membership#20002First.tenant by system and assume }", "{ grant role partner#10001:First.guest to role membership#20002:First.tenant by system and assume }",
"{ grant role debitor#10001First.guest to role membership#20002First.tenant by system and assume }", // "{ grant role debitor#1100First.guest to role membership#20002:First.tenant by system and assume }",
"{ grant role membership#20002First.tenant to role debitor#10001First.agent by system and assume }", // "{ grant role membership#20002:First.tenant to role debitor#1100First.agent by system and assume }",
"{ grant role membership#20002First.tenant to role partner#First.agent by system and assume }", "{ grant role debitor#1000111:First.guest to role membership#20002:First.tenant by system and assume }",
"{ grant role membership#20002:First.tenant to role debitor#1000111:First.agent by system and assume }",
"{ grant role membership#20002:First.tenant to role partner#10001:First.agent by system and assume }",
// guest // guest
"{ grant perm view on membership#20002First to role membership#20002First.guest by system and assume }", "{ grant perm view on membership#20002:First to role membership#20002:First.guest by system and assume }",
"{ grant role membership#20002First.guest to role membership#20002First.tenant by system and assume }", "{ grant role membership#20002:First.guest to role membership#20002:First.tenant by system and assume }",
"{ grant role membership#20002First.guest to role partner#First.tenant by system and assume }", "{ grant role membership#20002:First.guest to role partner#10001:First.tenant by system and assume }",
"{ grant role membership#20002First.guest to role debitor#10001First.tenant by system and assume }", // "{ grant role membership#20002:First.guest to role debitor#1100First.tenant by system and assume }",
"{ grant role membership#20002:First.guest to role debitor#1000111:First.tenant by system and assume }",
null)); null));
} }
@ -181,9 +185,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then // then
exactlyTheseMembershipsAreReturned( exactlyTheseMembershipsAreReturned(
result, result,
"Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)", "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)",
"Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)", "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)",
"Membership(10003, Third OHG, 10003, [2022-10-01,), NONE)"); "Membership(10003, Third OHG, 1000313, [2022-10-01,), NONE)");
} }
@Test @Test
@ -198,7 +202,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
null); null);
// then // then
exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)"); exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)");
} }
@Test @Test
@ -210,7 +214,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002); final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002);
// then // then
exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)"); exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)");
} }
} }
@ -224,7 +228,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var givenMembership = givenSomeTemporaryMembership("First", "First"); final var givenMembership = givenSomeTemporaryMembership("First", "First");
assertThatMembershipIsVisibleForUserWithRole( assertThatMembershipIsVisibleForUserWithRole(
givenMembership, givenMembership,
"hs_office_debitor#10001FirstGmbH-firstcontact.admin"); "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership); assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now(); final var newValidityEnd = LocalDate.now();
@ -251,13 +255,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var givenMembership = givenSomeTemporaryMembership("First", "First"); final var givenMembership = givenSomeTemporaryMembership("First", "First");
assertThatMembershipIsVisibleForUserWithRole( assertThatMembershipIsVisibleForUserWithRole(
givenMembership, givenMembership,
"hs_office_debitor#10001FirstGmbH-firstcontact.admin"); "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership); assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now(); final var newValidityEnd = LocalDate.now();
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_debitor#10001FirstGmbH-firstcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
givenMembership.setValidity(Range.closedOpen( givenMembership.setValidity(Range.closedOpen(
givenMembership.getValidity().lower(), newValidityEnd)); givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership); return membershipRepo.save(givenMembership);
@ -325,7 +329,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_debitor#10003ThirdOHG-thirdcontact.admin"); context("superuser-alex@hostsharing.net", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.admin");
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent(); assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent();
membershipRepo.deleteByUuid(givenMembership.getUuid()); membershipRepo.deleteByUuid(givenMembership.getUuid());
@ -382,8 +386,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then // then
assertThat(customerLogEntries).map(Arrays::toString).contains( assertThat(customerLogEntries).map(Arrays::toString).contains(
"[creating Membership test-data FirstGmbH10001, hs_office_membership, INSERT]", "[creating Membership test-data FirstGmbH11, hs_office_membership, INSERT]",
"[creating Membership test-data Seconde.K.10002, hs_office_membership, INSERT]"); "[creating Membership test-data Seconde.K.12, hs_office_membership, INSERT]");
} }
@BeforeEach @BeforeEach

View File

@ -19,12 +19,8 @@ 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;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.repository.HasUuid;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
@ -55,11 +51,12 @@ import static org.assertj.core.api.Assertions.assertThat;
* This 'test' includes the complete legacy 'office' data import. * This 'test' includes the complete legacy 'office' data import.
* *
* There is no code in 'main' because the import is not needed a normal runtime. * There is no code in 'main' because the import is not needed a normal runtime.
* There is some test data in Java resources to verfiy the data conversion. * There is some test data in Java resources to verify the data conversion.
* For a real import a main method will be added later * For a real import a main method will be added later
* which reads CSV files from the file system. * which reads CSV files from the file system.
*/ */
@Disabled
@DataJpaTest @DataJpaTest
@Import({ Context.class, JpaAttempt.class }) @Import({ Context.class, JpaAttempt.class })
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -324,14 +321,11 @@ public class ImportOfficeTables extends ContextBasedTest {
final var debitor = HsOfficeDebitorEntity.builder() final var debitor = HsOfficeDebitorEntity.builder()
.partner(partner) .partner(partner)
.debitorNumber(rec.getInteger("member_id")) // .debitorNumberSuffix(rec.getByte("member_id"))
// .memberCode(rec.get("member_code")) TODO: add as debitor_default_prefix to debitor // .defaultPrefix(rec.getString("member_code"))
.partner(partner) .partner(partner)
.billingContact(partner.getContact()) .billingContact(partner.getContact())
// .memberRoles(toBool(rec.get("member_role")) TODO: add negated as membership_billable to membership .billable(rec.isEmpty("free"))
// .authorContract(toBool(rec.get("author_contract")) not supported
// .nonDisclosureContract(toBool(rec.get("nondisc_contract")) not supported
// .free(toBool(rec.get("free")) TODO: add negated as billable to debitor
// .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor // .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor
.vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove
.vatId(rec.getString("uid_vat")) .vatId(rec.getString("uid_vat"))
@ -345,10 +339,11 @@ public class ImportOfficeTables extends ContextBasedTest {
.partner(partner) .partner(partner)
.memberNumber(rec.getInteger("member_id")) .memberNumber(rec.getInteger("member_id"))
.validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until"))) .validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until")))
.membershipFeeBillable(rec.isEmpty("member_role"))
.reasonForTermination( .reasonForTermination(
isBlank(rec.getString("member_until")) isBlank(rec.getString("member_until"))
? HsOfficeReasonForTermination.NONE ? HsOfficeReasonForTermination.NONE
: HsOfficeReasonForTermination.UNKNOWN) // TODO : HsOfficeReasonForTermination.UNKNOWN)
.mainDebitor(debitor) .mainDebitor(debitor)
.build(); .build();
memberships.put(rec.getInteger("bp_id"), membership); memberships.put(rec.getInteger("bp_id"), membership);
@ -619,9 +614,19 @@ class Record {
return row[columns.indexOf(columnName)]; return row[columns.indexOf(columnName)];
} }
boolean isEmpty(final String columnName) {
final String value = getString(columnName);
return value == null || value.isBlank();
}
Byte getByte(final String columnName) {
final String value = getString(columnName);
return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0;
}
Integer getInteger(final String columnName) { Integer getInteger(final String columnName) {
final String value = getString(columnName); final String value = getString(columnName);
return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; return isNotBlank(value) ? Integer.valueOf(value.trim()) : 0;
} }
BigDecimal getBigDecimal(final String columnName) { BigDecimal getBigDecimal(final String columnName) {
@ -639,5 +644,4 @@ class Record {
} }
return null; return null;
} }
} }

View File

@ -105,6 +105,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0);
final var newPartner = toCleanup(HsOfficePartnerEntity.builder() final var newPartner = toCleanup(HsOfficePartnerEntity.builder()
.debitorNumberPrefix(22222)
.person(givenPerson) .person(givenPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -115,11 +116,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// then // then
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.admin",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.agent", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.agent",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.owner",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.tenant",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.guest")); "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.guest"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())) assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess")) .map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("forthcontact", "4th"))
@ -127,31 +128,31 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// owner // owner
"{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }", "{ grant perm * on partner#22222:EBess-4th to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant perm * on partner_details#EBess-4th-details to role partner#EBess-4th.owner by system and assume }", "{ grant perm * on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }", "{ grant role partner#22222:EBess-4th.owner to role global#global.admin by system and assume }",
// admin // admin
"{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }", "{ grant perm edit on partner#22222:EBess-4th to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant perm edit on partner_details#EBess-4th-details to role partner#EBess-4th.admin by system and assume }", "{ grant perm edit on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }", "{ grant role partner#22222:EBess-4th.admin to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }", "{ grant role person#EBess.tenant to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }", "{ grant role contact#4th.tenant to role partner#22222:EBess-4th.admin by system and assume }",
// agent // agent
"{ grant perm view on partner_details#EBess-4th-details to role partner#EBess-4th.agent by system and assume }", "{ grant perm view on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.agent by system and assume }",
"{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role person#EBess.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role contact#4th.admin by system and assume }",
// tenant // tenant
"{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }", "{ grant role partner#22222:EBess-4th.tenant to role partner#22222:EBess-4th.agent by system and assume }",
"{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role person#EBess.guest to role partner#22222:EBess-4th.tenant by system and assume }",
"{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role contact#4th.guest to role partner#22222:EBess-4th.tenant by system and assume }",
// guest // guest
"{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }", "{ grant perm view on partner#22222:EBess-4th to role partner#22222:EBess-4th.guest by system and assume }",
"{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role partner#22222:EBess-4th.guest to role partner#22222:EBess-4th.tenant by system and assume }",
null)); null));
} }
@ -217,10 +218,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() { public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler("fifth contact"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "fifth contact");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_person#ErbenBesslerMelBessler.admin"); "hs_office_partner#22222:ErbenBesslerMelBessler-fifthcontact.admin");
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0); final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0);
@ -253,16 +254,16 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void partnerAgent_canNotUpdateRelatedPartner() { public void partnerAgent_canNotUpdateRelatedPartner() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler("ninth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
final var givenNewContact = contactRepo.findContactByOptionalLabelLike("tenth").get(0);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); context("superuser-alex@hostsharing.net",
"hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
givenPartner.getDetails().setBirthName("new birthname"); givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner); return partnerRepo.save(givenPartner);
}); });
@ -304,7 +305,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() { public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler("tenth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "tenth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -324,7 +325,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() { public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler("eleventh"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "eleventh");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -350,7 +351,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll()));
final var givenPartner = givenSomeTemporaryPartnerBessler("twelfth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "twelfth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -394,12 +395,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
}); });
} }
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final String contact) { private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(
final Integer debitorNumberPrefix, final String person, final String contact) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.debitorNumberPrefix(debitorNumberPrefix)
.person(givenPerson) .person(givenPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())

View File

@ -8,10 +8,11 @@ import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGA
public class TestHsOfficePartner { public class TestHsOfficePartner {
public static final HsOfficePartnerEntity TEST_PARTNER = HsOfficePartnerWithLegalPerson("Test Ltd."); public static final HsOfficePartnerEntity TEST_PARTNER = hsOfficePartnerWithLegalPerson("Test Ltd.");
static public HsOfficePartnerEntity HsOfficePartnerWithLegalPerson(final String tradeName) { static public HsOfficePartnerEntity hsOfficePartnerWithLegalPerson(final String tradeName) {
return HsOfficePartnerEntity.builder() return HsOfficePartnerEntity.builder()
.debitorNumberPrefix(10001)
.person(HsOfficePersonEntity.builder() .person(HsOfficePersonEntity.builder()
.personType(LEGAL) .personType(LEGAL)
.tradeName(tradeName) .tradeName(tradeName)

View File

@ -80,7 +80,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
[ [
{ {
"debitor": { "debitor": {
"debitorNumber": 10002, "debitorNumber": 1000212,
"billingContact": { "label": "second contact" } "billingContact": { "label": "second contact" }
}, },
"bankAccount": { "holder": "Second e.K." }, "bankAccount": { "holder": "Second e.K." },
@ -90,7 +90,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
}, },
{ {
"debitor": { "debitor": {
"debitorNumber": 10001, "debitorNumber": 1000111,
"billingContact": { "label": "first contact" } "billingContact": { "label": "first contact" }
}, },
"bankAccount": { "holder": "First GmbH" }, "bankAccount": { "holder": "First GmbH" },
@ -100,7 +100,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
}, },
{ {
"debitor": { "debitor": {
"debitorNumber": 10003, "debitorNumber": 1000313,
"billingContact": { "label": "third contact" } "billingContact": { "label": "third contact" }
}, },
"bankAccount": { "holder": "Third OHG" }, "bankAccount": { "holder": "Third OHG" },
@ -269,7 +269,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
{ {
"debitor": { "debitor": {
"debitorNumber": 10001, "debitorNumber": 1000111,
"billingContact": { "label": "first contact" } "billingContact": { "label": "first contact" }
}, },
"bankAccount": { "bankAccount": {
@ -321,7 +321,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
.body("", lenientlyEquals(""" .body("", lenientlyEquals("""
{ {
"debitor": { "debitor": {
"debitorNumber": 10001, "debitorNumber": 1000111,
"billingContact": { "label": "first contact" } "billingContact": { "label": "first contact" }
}, },
"bankAccount": { "bankAccount": {
@ -376,7 +376,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: First GmbH)"); assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z - patched");
assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05"); assertThat(mandate.getValidFrom()).isEqualTo("2020-06-05");
@ -417,7 +417,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
// finally, the sepaMandate is actually updated // finally, the sepaMandate is actually updated
assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get() assertThat(sepaMandateRepo.findByUuid(givenSepaMandate.getUuid())).isPresent().get()
.matches(mandate -> { .matches(mandate -> {
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: First GmbH)"); assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(1000111: First GmbH)");
assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH"); assertThat(mandate.getBankAccount().toShortString()).isEqualTo("First GmbH");
assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z"); assertThat(mandate.getReference()).isEqualTo("temp ref CAT Z");
assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)"); assertThat(mandate.getValidity().asString()).isEqualTo("[2022-11-01,2023-01-01)");

View File

@ -144,13 +144,13 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest {
// agent // agent
"{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }", "{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }",
"{ grant role debitor#10001FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }", "{ grant role debitor#1000111:FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }",
"{ grant role sepamandate#temprefB.agent to role bankaccount#Paul....admin by system and assume }", "{ grant role sepamandate#temprefB.agent to role bankaccount#Paul....admin by system and assume }",
"{ grant role sepamandate#temprefB.agent to role debitor#10001FirstGmbH-....admin by system and assume }", "{ grant role sepamandate#temprefB.agent to role debitor#1000111:FirstGmbH-....admin by system and assume }",
// tenant // tenant
"{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }", "{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }",
"{ grant role debitor#10001FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }", "{ grant role debitor#1000111:FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }",
"{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }", "{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
// guest // guest

View File

@ -1,4 +1,4 @@
bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat
7;10007;mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 7;10007;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007
10;10010;xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; 10;10010;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS;
12;11012;xxx;2021-04-01;;;;;true;true;GROSS; 12;11012;hsh00-xxx;2021-04-01;;;;;true;true;GROSS;

1 bp_id member_id member_code member_since member_until member_role author_contract nondisc_contract free exempt_vat indicator_vat uid_vat
2 7 10007 hsh00-mih 2000-12-06 Aufsichtsrat 2006-10-15 2001-10-15 false false NET DE-VAT-007
3 10 10010 hsh00-xyz 2000-12-06 2015-12-31 false false GROSS
4 12 11012 hsh00-xxx 2021-04-01 true true GROSS