add: membership-fee-billable, debitor.billable, debitor.billingContact, debitor.defaultPrefix

This commit is contained in:
Michael Hoennig 2024-01-09 12:11:56 +01:00
parent 7e31e95d57
commit 980524e7ab
39 changed files with 435 additions and 260 deletions

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact;
import lombok.*;
import lombok.experimental.FieldNameConstants;
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.Stringifyable;
import org.hibernate.annotations.GenericGenerator;

View File

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

View File

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

View File

@ -40,13 +40,16 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@JoinColumn(name = "partneruuid")
private HsOfficePartnerEntity partner;
@Column(name = "debitornumber")
private Integer debitorNumber;
@Column(name = "debitornumbersuffix", columnDefinition = "numeric(2)")
private Byte debitorNumberSuffix; // TODO maybe rather as a formatted String?
@ManyToOne
@JoinColumn(name = "billingcontactuuid")
private HsOfficeContactEntity billingContact;
@Column(name = "billable")
private boolean billable;
@Column(name = "vatid")
private String vatId;
@ -60,6 +63,17 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@JoinColumn(name = "refundbankaccountuuid")
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
public String toString() {
return stringify.apply(this);
@ -67,6 +81,6 @@ public class HsOfficeDebitorEntity implements Stringifyable {
@Override
public String toShortString() {
return debitorNumber.toString();
return getDebitorNumberString();
}
}

View File

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

View File

@ -13,9 +13,14 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
@Query("""
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("""
SELECT debitor FROM HsOfficeDebitorEntity debitor

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -31,6 +31,7 @@ call create_journal('hs_office_partner_details');
create table hs_office_partner
(
uuid uuid unique references RbacObject (uuid) initially deferred,
debitorNumberPrefix varchar(5),
personUuid uuid not null references hs_office_person(uuid),
contactUuid uuid not null references hs_office_contact(uuid),
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
strict as $$
declare
hsOfficePartnerTenant RbacRoleDescriptor;
oldPerson hs_office_person;
newPerson hs_office_person;
oldContact hs_office_contact;
@ -166,6 +165,8 @@ execute procedure hsOfficePartnerRbacRolesTrigger();
--changeset hs-office-partner-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
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_contact_iv c where c.uuid = target.contactuuid)

View File

@ -8,7 +8,10 @@
/*
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 $$
declare
currentTask varchar;
@ -51,8 +54,8 @@ begin
end if;
insert
into hs_office_partner (uuid, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
into hs_office_partner (uuid, debitorNumberPrefix, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), debitorNumberPrefix, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
end; $$;
--//
@ -64,11 +67,11 @@ end; $$;
do language plpgsql $$
begin
call createHsOfficePartnerTestData('First GmbH', 'first contact');
call createHsOfficePartnerTestData('Second e.K.', 'second contact');
call createHsOfficePartnerTestData('Third OHG', 'third contact');
call createHsOfficePartnerTestData('Fourth e.G.', 'forth contact');
call createHsOfficePartnerTestData('Smith', 'fifth contact');
call createHsOfficePartnerTestData(10001, 'First GmbH', 'first contact');
call createHsOfficePartnerTestData(10002, 'Second e.K.', 'second contact');
call createHsOfficePartnerTestData(10003, 'Third OHG', 'third contact');
call createHsOfficePartnerTestData(10004, 'Fourth e.G.', 'forth contact');
call createHsOfficePartnerTestData(10010, 'Smith', 'fifth contact');
end;
$$;
--//

View File

@ -8,12 +8,17 @@ create table hs_office_debitor
(
uuid uuid unique references RbacObject (uuid) initially deferred,
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),
vatId varchar(24), -- TODO.spec: here or in person?
vatCountryCode varchar(2),
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
);
--//

View File

@ -172,8 +172,10 @@ execute procedure hsOfficeDebitorRbacRolesTrigger();
--changeset hs-office-debitor-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
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$);
--//
@ -181,14 +183,17 @@ call generateRbacIdentityView('hs_office_debitor', $idName$
-- ============================================================================
--changeset hs-office-debitor-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumber',
call generateRbacRestrictedView('hs_office_debitor', 'target.debitorNumberSuffix',
$updates$
partnerUuid = new.partnerUuid,
billable = new.billable,
billingContactUuid = new.billingContactUuid,
debitorNumberSuffix = new.debitorNumberSuffix,
refundBankAccountUuid = new.refundBankAccountUuid,
vatId = new.vatId,
vatCountryCode = new.vatCountryCode,
vatBusiness = new.vatBusiness
vatBusiness = new.vatBusiness,
defaultPrefix = new.defaultPrefix
$updates$);
--//

View File

@ -8,7 +8,12 @@
/*
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 $$
declare
currentTask varchar;
@ -16,7 +21,6 @@ declare
relatedPartner hs_office_partner;
relatedContact hs_office_contact;
relatedBankAccountUuid uuid;
newDebitorNumber numeric(6);
begin
idName := cleanIdentifier( partnerTradeName|| '-' || billingContactLabel);
currentTask := 'creating debitor test-data ' || idName;
@ -28,14 +32,14 @@ begin
where person.tradeName = partnerTradeName into relatedPartner;
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 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 billingContact (%): %', relatedContact.uuid, relatedContact;
insert
into hs_office_debitor (uuid, partneruuid, debitornumber, billingcontactuuid, vatbusiness, refundbankaccountuuid)
values (uuid_generate_v4(), relatedPartner.uuid, newDebitorNumber, relatedContact.uuid, true, relatedBankAccountUuid);
into hs_office_debitor (uuid, partneruuid, debitornumbersuffix, billable, billingcontactuuid, vatbusiness, refundbankaccountuuid, defaultprefix)
values (uuid_generate_v4(), relatedPartner.uuid, debitorNumberSuffix, true, relatedContact.uuid, true, relatedBankAccountUuid, defaultPrefix);
end; $$;
--//
@ -46,9 +50,9 @@ end; $$;
do language plpgsql $$
begin
call createHsOfficeDebitorTestData('First GmbH', 'first contact');
call createHsOfficeDebitorTestData('Second e.K.', 'second contact');
call createHsOfficeDebitorTestData('Third OHG', 'third contact');
call createHsOfficeDebitorTestData(11, 'First GmbH', 'first contact', 'fir');
call createHsOfficeDebitorTestData(12, 'Second e.K.', 'second contact', 'sec');
call createHsOfficeDebitorTestData(13, 'Third OHG', 'third contact', 'thi');
end;
$$;
--//

View File

@ -15,7 +15,8 @@ create table if not exists hs_office_membership
mainDebitorUuid uuid not null references hs_office_debitor(uuid),
memberNumber numeric(5) not null unique,
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:--//
-- ----------------------------------------------------------------------------
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$);
--//
@ -104,7 +105,8 @@ call generateRbacRestrictedView('hs_office_membership',
orderby => 'target.memberNumber',
columnUpdates => $updates$
validity = new.validity,
reasonForTermination = new.reasonForTermination
reasonForTermination = new.reasonForTermination,
membership_fee_billable = new.membership_fee_billable
$updates$);
--//

View File

@ -8,11 +8,14 @@
/*
Creates a single membership test record.
*/
-- create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber integer )
create or replace procedure createHsOfficeMembershipTestData( forPartnerTradeName varchar, forMainDebitorNumber numeric )
language plpgsql as $$
declare
currentTask varchar;
idName varchar;
-- forDebitorNumberPrefix integer;
-- forDebitorNumberSuffix integer;
relatedPartner hs_office_partner;
relatedDebitor hs_office_debitor;
newMemberNumber numeric;
@ -25,7 +28,10 @@ begin
select partner.* from hs_office_partner partner
join hs_office_person person on person.uuid = partner.personUuid
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;
raise notice 'creating test Membership: %', idName;
@ -33,6 +39,7 @@ begin
raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor;
insert
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');
end; $$;
--//
@ -44,9 +51,14 @@ end; $$;
do language plpgsql $$
begin
call createHsOfficeMembershipTestData('First GmbH', 10001);
call createHsOfficeMembershipTestData('Second e.K.', 10002);
call createHsOfficeMembershipTestData('Third OHG', 10003);
call createHsOfficeMembershipTestData('First GmbH', 11);
call createHsOfficeMembershipTestData('Second e.K.', 12);
call createHsOfficeMembershipTestData('Third OHG', 13);
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()
.that().resideInAPackage("..hs.office.coopshares..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage("..hs.office.coopshares..");
.resideInAnyPackage(
"..hs.office.coopshares..",
"..hs.office.migration..");
@ArchTest
@SuppressWarnings("unused")

View File

@ -117,7 +117,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
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));
}
@ -200,8 +200,7 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() {
// given:
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.admin",
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
// when:
final var result = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(

View File

@ -116,7 +116,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
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));
}
@ -199,8 +199,7 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given:
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.admin",
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
// when:
final var result = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(

View File

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

View File

@ -13,30 +13,50 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder()
.debitorNumber(123456)
.debitorNumberSuffix((byte)67)
.partner(HsOfficePartnerEntity.builder()
.person(HsOfficePersonEntity.builder()
.tradeName("some trade name")
.build())
.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(123456: some trade name)");
assertThat(result).isEqualTo("debitor(1234567: some trade name)");
}
@Test
void toShortStringContainsPartnerAndContact() {
void toStringWithoutPersonContainsDebitorNumber() {
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();
final var result = given.toShortString();
assertThat(result).isEqualTo("123456");
assertThat(result).isEqualTo("1234567");
}
}

View File

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

View File

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

View File

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

View File

@ -27,7 +27,8 @@ class HsOfficeMembershipEntityUnitTest {
void toStringContainsAllProps() {
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

View File

@ -119,11 +119,11 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var all = rawRoleRepo.findAll();
assertThat(roleNamesOf(all)).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
"hs_office_membership#20002FirstGmbH-firstcontact.admin",
"hs_office_membership#20002FirstGmbH-firstcontact.agent",
"hs_office_membership#20002FirstGmbH-firstcontact.guest",
"hs_office_membership#20002FirstGmbH-firstcontact.owner",
"hs_office_membership#20002FirstGmbH-firstcontact.tenant"));
"hs_office_membership#20002:FirstGmbH-firstcontact.admin",
"hs_office_membership#20002:FirstGmbH-firstcontact.agent",
"hs_office_membership#20002:FirstGmbH-firstcontact.guest",
"hs_office_membership#20002:FirstGmbH-firstcontact.owner",
"hs_office_membership#20002:FirstGmbH-firstcontact.tenant"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
@ -131,32 +131,36 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
initialGrantNames,
// owner
"{ grant perm * on membership#20002First to role membership#20002First.owner by system and assume }",
"{ grant role membership#20002First.owner to role global#global.admin by system and assume }",
"{ grant perm * on membership#20002:First to role membership#20002:First.owner by system and assume }",
"{ grant role membership#20002:First.owner to role global#global.admin by system and assume }",
// admin
"{ grant perm edit on membership#20002First to role membership#20002First.admin by system and assume }",
"{ grant role membership#20002First.admin to role membership#20002First.owner by system and assume }",
"{ grant perm edit on membership#20002:First to role membership#20002:First.admin by system and assume }",
"{ grant role membership#20002:First.admin to role membership#20002:First.owner by system and assume }",
// agent
"{ grant role membership#20002First.agent to role membership#20002First.admin by system and assume }",
"{ grant role partner#First.tenant to role membership#20002First.agent by system and assume }",
"{ grant role membership#20002First.agent to role debitor#10001First.admin by system and assume }",
"{ grant role membership#20002First.agent to role partner#First.admin by system and assume }",
"{ grant role debitor#10001First.tenant to role membership#20002First.agent by system and assume }",
"{ grant role membership#20002:First.agent to role membership#20002:First.admin by system and assume }",
"{ grant role partner#10001:First.tenant to role membership#20002:First.agent by system and assume }",
"{ grant role membership#20002:First.agent to role debitor#1000111: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#1000111:First.tenant to role membership#20002:First.agent by system and assume }",
// tenant
"{ grant role membership#20002First.tenant to role membership#20002First.agent by system and assume }",
"{ grant role partner#First.guest to role membership#20002First.tenant by system and assume }",
"{ grant role debitor#10001First.guest to role membership#20002First.tenant by system and assume }",
"{ grant role membership#20002First.tenant to role debitor#10001First.agent by system and assume }",
"{ grant role membership#20002First.tenant to role partner#First.agent by system and assume }",
"{ grant role membership#20002:First.tenant to role membership#20002:First.agent by system and assume }",
"{ grant role partner#10001:First.guest to role membership#20002:First.tenant by system and assume }",
// "{ grant role debitor#1100First.guest to role membership#20002:First.tenant by system and assume }",
// "{ grant role membership#20002:First.tenant to role debitor#1100First.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
"{ grant perm view on membership#20002First to role membership#20002First.guest by system and assume }",
"{ grant role membership#20002First.guest to role membership#20002First.tenant by system and assume }",
"{ grant role membership#20002First.guest to role partner#First.tenant by system and assume }",
"{ grant role membership#20002First.guest to role debitor#10001First.tenant by system and assume }",
"{ grant perm view on membership#20002:First to role membership#20002:First.guest by system and assume }",
"{ grant role membership#20002:First.guest to role membership#20002: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#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));
}
@ -181,9 +185,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseMembershipsAreReturned(
result,
"Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)",
"Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)",
"Membership(10003, Third OHG, 10003, [2022-10-01,), NONE)");
"Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)",
"Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)",
"Membership(10003, Third OHG, 1000313, [2022-10-01,), NONE)");
}
@Test
@ -198,7 +202,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
null);
// then
exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)");
exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)");
}
@Test
@ -210,7 +214,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002);
// 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");
assertThatMembershipIsVisibleForUserWithRole(
givenMembership,
"hs_office_debitor#10001FirstGmbH-firstcontact.admin");
"hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now();
@ -251,13 +255,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var givenMembership = givenSomeTemporaryMembership("First", "First");
assertThatMembershipIsVisibleForUserWithRole(
givenMembership,
"hs_office_debitor#10001FirstGmbH-firstcontact.admin");
"hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now();
// when
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.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);
@ -325,7 +329,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// when
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();
membershipRepo.deleteByUuid(givenMembership.getUuid());
@ -382,8 +386,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
"[creating Membership test-data FirstGmbH10001, hs_office_membership, INSERT]",
"[creating Membership test-data Seconde.K.10002, hs_office_membership, INSERT]");
"[creating Membership test-data FirstGmbH11, hs_office_membership, INSERT]",
"[creating Membership test-data Seconde.K.12, hs_office_membership, INSERT]");
}
@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.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.repository.HasUuid;
import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
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.
*
* 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
* which reads CSV files from the file system.
*/
@Disabled
@DataJpaTest
@Import({ Context.class, JpaAttempt.class })
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -324,14 +321,11 @@ public class ImportOfficeTables extends ContextBasedTest {
final var debitor = HsOfficeDebitorEntity.builder()
.partner(partner)
.debitorNumber(rec.getInteger("member_id"))
// .memberCode(rec.get("member_code")) TODO: add as debitor_default_prefix to debitor
// .debitorNumberSuffix(rec.getByte("member_id"))
// .defaultPrefix(rec.getString("member_code"))
.partner(partner)
.billingContact(partner.getContact())
// .memberRoles(toBool(rec.get("member_role")) TODO: add negated as membership_billable to membership
// .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
.billable(rec.isEmpty("free"))
// .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
.vatId(rec.getString("uid_vat"))
@ -345,10 +339,11 @@ public class ImportOfficeTables extends ContextBasedTest {
.partner(partner)
.memberNumber(rec.getInteger("member_id"))
.validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until")))
.membershipFeeBillable(rec.isEmpty("member_role"))
.reasonForTermination(
isBlank(rec.getString("member_until"))
? HsOfficeReasonForTermination.NONE
: HsOfficeReasonForTermination.UNKNOWN) // TODO
: HsOfficeReasonForTermination.UNKNOWN)
.mainDebitor(debitor)
.build();
memberships.put(rec.getInteger("bp_id"), membership);
@ -619,9 +614,19 @@ class Record {
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) {
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) {
@ -639,5 +644,4 @@ class Record {
}
return null;
}
}

View File

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

View File

@ -8,10 +8,11 @@ import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGA
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()
.debitorNumberPrefix(10001)
.person(HsOfficePersonEntity.builder()
.personType(LEGAL)
.tradeName(tradeName)

View File

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

View File

@ -144,13 +144,13 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest {
// agent
"{ 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 debitor#10001FirstGmbH-....admin by system and assume }",
"{ grant role sepamandate#temprefB.agent to role debitor#1000111:FirstGmbH-....admin by system and assume }",
// tenant
"{ 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 }",
// 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
7;10007;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;
12;11012;xxx;2021-04-01;;;;;true;true;GROSS;
7;10007;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007
10;10010;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;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 mih hsh00-mih 2000-12-06 Aufsichtsrat 2006-10-15 2001-10-15 false false NET DE-VAT-007
3 10 10010 xyz hsh00-xyz 2000-12-06 2015-12-31 false false GROSS
4 12 11012 xxx hsh00-xxx 2021-04-01 true true GROSS