Compare commits

..

7 Commits

Author SHA1 Message Date
Michael Hoennig
2aeb8fef6f introduce view tx_journal_v combining tx_journal with tx_context 2024-01-25 16:52:41 +01:00
Michael Hoennig
3c2f607f0b aftermaths from merging branch add-subscriber-role 2024-01-25 16:18:24 +01:00
Michael Hoennig
8f4ac8b2bf Merge branch 'add-subscriber-role' into introduce-partner-business-role
# Conflicts:
#	src/main/resources/db/changelog/228-hs-office-relationship-test-data.sql
#	src/test/java/net/hostsharing/hsadminng/hs/office/migration/ImportOfficeData.java
2024-01-25 16:15:17 +01:00
Michael Hoennig
17a73918f5 add test-data and include relMark in toString 2024-01-25 16:00:32 +01:00
Michael Hoennig
82573d1c9f fix relType, which was not mapped by OpenApi anymore of there is relTypeMark, by renaming relTypeMark to relMark - strange! 2024-01-25 15:30:06 +01:00
Michael Hoennig
53c3b04ba1 add subscriber role and relTypeMark and import these roles 2024-01-25 15:06:33 +01:00
Michael Hoennig
10cd4bd0e8 WIP 2024-01-25 13:42:28 +01:00
23 changed files with 189 additions and 62 deletions

View File

@ -0,0 +1,14 @@
# Sketch UI for mailinglist subscriptions
```PlantUML
@startsalt
{{^==Mailinglist-Subscriptions
Members-Announce: | [<color:#9a9a9a>X</color>] | ^frank.meiler@example.org^ | [Create New]
Members-Discussion: | [X] | ^frank.meiler@example.org^ | [Create New]
Customers-Announce: | [<color:#9a9a9a>X</color>] | ^frank.meiler@example.org^ | [Create New]
Operations-Announce: | [<color:#9a9a9a>X</color>] | ^Default Contact Data^ | [Create New]
Operations-Discussion: | [ ] | ^ ^ | [Create New]
}}
@endsalt
```

View File

@ -26,6 +26,7 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
private static Stringify<HsOfficeRelationshipEntity> toString = stringify(HsOfficeRelationshipEntity.class, "rel") private static Stringify<HsOfficeRelationshipEntity> toString = stringify(HsOfficeRelationshipEntity.class, "rel")
.withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor) .withProp(Fields.relAnchor, HsOfficeRelationshipEntity::getRelAnchor)
.withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType) .withProp(Fields.relType, HsOfficeRelationshipEntity::getRelType)
.withProp(Fields.relMark, HsOfficeRelationshipEntity::getRelMark)
.withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder) .withProp(Fields.relHolder, HsOfficeRelationshipEntity::getRelHolder)
.withProp(Fields.contact, HsOfficeRelationshipEntity::getContact); .withProp(Fields.contact, HsOfficeRelationshipEntity::getContact);
@ -54,6 +55,9 @@ public class HsOfficeRelationshipEntity implements HasUuid, Stringifyable {
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private HsOfficeRelationshipType relType; private HsOfficeRelationshipType relType;
@Column(name = "relmark")
private String relMark;
@Override @Override
public String toString() { public String toString() {
return toString.apply(this); return toString.apply(this);

View File

@ -7,5 +7,6 @@ public enum HsOfficeRelationshipType {
REPRESENTATIVE, REPRESENTATIVE,
VIP_CONTACT, VIP_CONTACT,
ACCOUNTING, ACCOUNTING,
OPERATIONS OPERATIONS,
SUBSCRIBER
} }

View File

@ -12,6 +12,7 @@ components:
- VIP_CONTACT - VIP_CONTACT
- ACCOUNTING, - ACCOUNTING,
- OPERATIONS - OPERATIONS
- SUBSCRIBER
HsOfficeRelationship: HsOfficeRelationship:
type: object type: object
@ -25,6 +26,9 @@ components:
$ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson' $ref: './hs-office-person-schemas.yaml#/components/schemas/HsOfficePerson'
relType: relType:
type: string type: string
relMark:
type: string
nullable: true
contact: contact:
$ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact' $ref: './hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
@ -48,6 +52,8 @@ components:
relType: relType:
type: string type: string
nullable: true nullable: true
relMark:
type: string
contactUuid: contactUuid:
type: string type: string
format: uuid format: uuid

View File

@ -53,6 +53,19 @@ create table tx_journal
create index on tx_journal (targetTable, targetUuid); create index on tx_journal (targetTable, targetUuid);
--// --//
-- ============================================================================
--changeset audit-TX-JOURNAL-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
A view combining tx_journal with tx_context.
*/
create view tx_journal_v as
select txc.*, txj.targettable, txj.targetop, txj.targetuuid, txj.targetdelta
from tx_journal txj
left join tx_context txc using (contextid)
order by txc.txtimestamp;
--//
-- ============================================================================ -- ============================================================================
--changeset audit-TX-JOURNAL-TRIGGER:1 endDelimiter:--// --changeset audit-TX-JOURNAL-TRIGGER:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------

View File

@ -11,7 +11,8 @@ CREATE TYPE HsOfficeRelationshipType AS ENUM (
'REPRESENTATIVE', 'REPRESENTATIVE',
'VIP_CONTACT', 'VIP_CONTACT',
'ACCOUNTING', 'ACCOUNTING',
'OPERATIONS'); 'OPERATIONS',
'SUBSCRIBER');
CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT; CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT;
@ -21,7 +22,8 @@ create table if not exists hs_office_relationship
relAnchorUuid uuid not null references hs_office_person(uuid), relAnchorUuid uuid not null references hs_office_person(uuid),
relHolderUuid uuid not null references hs_office_person(uuid), relHolderUuid uuid not null references hs_office_person(uuid),
contactUuid uuid references hs_office_contact(uuid), contactUuid uuid references hs_office_contact(uuid),
relType HsOfficeRelationshipType not null relType HsOfficeRelationshipType not null,
relMark varchar(24)
); );
--// --//

View File

@ -12,7 +12,8 @@ create or replace procedure createHsOfficeRelationshipTestData(
holderPersonName varchar, holderPersonName varchar,
relationshipType HsOfficeRelationshipType, relationshipType HsOfficeRelationshipType,
anchorPersonTradeName varchar, anchorPersonTradeName varchar,
contactLabel varchar) contactLabel varchar,
mark varchar default null)
language plpgsql as $$ language plpgsql as $$
declare declare
currentTask varchar; currentTask varchar;
@ -49,8 +50,8 @@ begin
raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson; raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson;
raise notice '- using contact (%): %', contact.uuid, contact; raise notice '- using contact (%): %', contact.uuid, contact;
insert insert
into hs_office_relationship (uuid, relanchoruuid, relholderuuid, reltype, contactUuid) into hs_office_relationship (uuid, relanchoruuid, relholderuuid, reltype, relmark, contactUuid)
values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationshipType, contact.uuid); values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationshipType, mark, contact.uuid);
end; $$; end; $$;
--// --//
@ -97,7 +98,7 @@ do language plpgsql $$
call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact'); call createHsOfficeRelationshipTestData('Fouler', 'REPRESENTATIVE', 'Third OHG', 'third contact');
call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact'); call createHsOfficeRelationshipTestData('Smith', 'PARTNER', 'Hostsharing eG', 'sixth contact');
call createHsOfficeRelationshipTestData('Smith', 'SUBSCRIBER', 'Third OHG', 'third contact', 'members-announce');
end; end;
$$; $$;
--// --//

View File

@ -33,7 +33,7 @@ create table hs_office_partner
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
partnerNumber numeric(5), partnerNumber numeric(5),
partnerRoleUuid uuid not null references hs_office_relationship(uuid), partnerRoleUuid uuid not null references hs_office_relationship(uuid) on delete cascade,
personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid personUuid uuid not null references hs_office_person(uuid), -- TODO: remove, replaced by partnerRoleUuid
contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid contactUuid uuid not null references hs_office_contact(uuid), -- TODO: remove, replaced by partnerRoleUuid
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,12 +27,17 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
oldPartnerRole hs_office_relationship;
newPartnerRole hs_office_relationship;
oldPerson hs_office_person; oldPerson hs_office_person;
newPerson hs_office_person; newPerson hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
newContact hs_office_contact; newContact hs_office_contact;
begin begin
select * from hs_office_relationship as r where r.uuid = NEW.partnerroleuuid into newPartnerRole;
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson; select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
@ -52,6 +57,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)], hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonTenant(newPerson), hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)] hsOfficeContactTenant(newContact)]
); );
@ -60,6 +66,7 @@ begin
hsOfficePartnerAgent(NEW), hsOfficePartnerAgent(NEW),
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAdmin(NEW), hsOfficePartnerAdmin(NEW),
hsOfficeRelationshipAdmin(newPartnerRole),
hsOfficePersonAdmin(newPerson), hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)] hsOfficeContactAdmin(newContact)]
); );
@ -69,6 +76,7 @@ begin
incomingSuperRoles => array[ incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)], hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[ outgoingSubRoles => array[
hsOfficeRelationshipTenant(newPartnerRole),
hsOfficePersonGuest(newPerson), hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)] hsOfficeContactGuest(newContact)]
); );
@ -109,6 +117,19 @@ begin
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
if OLD.partnerRoleUuid <> NEW.partnerRoleUuid then
select * from hs_office_relationship as r where r.uuid = OLD.partnerRoleUuid into oldPartnerRole;
call revokeRoleFromRole(hsOfficeRelationshipTenant(oldPerson), hsOfficePartnerAdmin(OLD));
call grantRoleToRole(hsOfficeRelationshipTenant(newPerson), hsOfficePartnerAdmin(NEW));
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeRelationshipAdmin(oldPerson));
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeRelationshipAdmin(newPerson));
call revokeRoleFromRole(hsOfficeRelationshipGuest(oldPerson), hsOfficePartnerTenant(OLD));
call grantRoleToRole(hsOfficeRelationshipGuest(newPerson), hsOfficePartnerTenant(NEW));
end if;
if OLD.personUuid <> NEW.personUuid then if OLD.personUuid <> NEW.personUuid then
select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson; select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
@ -179,7 +200,7 @@ call generateRbacIdentityView('hs_office_partner', $idName$
call generateRbacRestrictedView('hs_office_partner', call generateRbacRestrictedView('hs_office_partner',
'(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)',
$updates$ $updates$
partherRoleUuid = new.partnerRoleUuid, partnerRoleUuid = new.partnerRoleUuid,
personUuid = new.personUuid, personUuid = new.personUuid,
contactUuid = new.contactUuid contactUuid = new.contactUuid
$updates$); $updates$);
@ -191,7 +212,7 @@ call generateRbacRestrictedView('hs_office_partner',
--changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--// --changeset hs-office-partner-rbac-NEW-PARTNER:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
Creates a global permission for new-partner and assigns it to the hostsharing admins role. Creates a global permission for new-partner and assigns it to the Hostsharing admins role.
*/ */
do language plpgsql $$ do language plpgsql $$
declare declare

View File

@ -279,9 +279,8 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_bankaccount'; where targettable = 'hs_office_bankaccount';
"""); """);

View File

@ -259,9 +259,8 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_contact'; where targettable = 'hs_office_contact';
"""); """);

View File

@ -216,9 +216,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_coopassetstransaction'; where targettable = 'hs_office_coopassetstransaction';
"""); """);

View File

@ -215,9 +215,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_coopsharestransaction'; where targettable = 'hs_office_coopsharestransaction';
"""); """);

View File

@ -548,10 +548,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId where targettable = 'hs_office_coopsharestransaction';
where targettable = 'hs_office_debitor';
"""); """);
// when // when

View File

@ -376,9 +376,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_membership'; where targettable = 'hs_office_membership';
"""); """);

View File

@ -22,6 +22,7 @@ import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEnti
import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType; import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
@ -113,6 +114,17 @@ import static org.assertj.core.api.Fail.fail;
@ExtendWith(OrderedDependedTestsExtension.class) @ExtendWith(OrderedDependedTestsExtension.class)
public class ImportOfficeData extends ContextBasedTest { public class ImportOfficeData extends ContextBasedTest {
private static final String[] SUBSCRIBER_ROLES = new String[] {
"subscriber:operations-discussion",
"subscriber:operations-announce",
"subscriber:members-announce",
"subscriber:members-discussion",
"subscriber:customers-announce"
};
private static final String[] KNOWN_ROLES = ArrayUtils.addAll(
new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"},
SUBSCRIBER_ROLES);
static int relationshipId = 2000000; static int relationshipId = 2000000;
@Value("${spring.datasource.url}") @Value("${spring.datasource.url}")
@ -257,10 +269,13 @@ public class ImportOfficeData extends ContextBasedTest {
2000004=rel(relAnchor='LP JM GmbH', relType='EX_PARTNER', relHolder='LP JM e.K.', contact='JM e.K.'), 2000004=rel(relAnchor='LP JM GmbH', relType='EX_PARTNER', relHolder='LP JM e.K.', contact='JM e.K.'),
2000005=rel(relAnchor='LP JM GmbH', relType='OPERATIONS', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000005=rel(relAnchor='LP JM GmbH', relType='OPERATIONS', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000006=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), 2000006=rel(relAnchor='LP JM GmbH', relType='VIP_CONTACT', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000007=rel(relAnchor='LP JM GmbH', relType='REPRESENTATIVE', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), 2000007=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='operations-announce', relHolder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
2000008=rel(relAnchor='?? Test PS', relType='OPERATIONS', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000008=rel(relAnchor='LP JM GmbH', relType='REPRESENTATIVE', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000009=rel(relAnchor='?? Test PS', relType='REPRESENTATIVE', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'), 2000009=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='members-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000010=rel(relAnchor='NP Mellies, Michael', relType='REPRESENTATIVE', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies ') 2000010=rel(relAnchor='LP JM GmbH', relType='SUBSCRIBER', relMark='customers-announce', relHolder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
2000011=rel(relAnchor='?? Test PS', relType='OPERATIONS', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000012=rel(relAnchor='?? Test PS', relType='REPRESENTATIVE', relHolder='?? Test PS', contact='Petra Schmidt , Test PS'),
2000013=rel(relAnchor='NP Mellies, Michael', relType='REPRESENTATIVE', relHolder='NP Mellies, Michael', contact='Herr Michael Mellies ')
} }
"""); """);
} }
@ -763,7 +778,14 @@ public class ImportOfficeData extends ContextBasedTest {
if (containsRole(rec, "vip-contact")) { if (containsRole(rec, "vip-contact")) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT); addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.VIP_CONTACT);
} }
verifyContainsOnly(rec.getString("roles"), "partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"); for (String subscriberRole: SUBSCRIBER_ROLES) {
if (containsRole(rec, subscriberRole)) {
addRelationship(partnerPerson, contactPerson, contact, HsOfficeRelationshipType.SUBSCRIBER)
.setRelMark(subscriberRole.split(":")[1])
;
}
}
verifyContainsOnlyKnownRoles(rec.getString("roles"));
}); });
optionallyAddMissingContractualRelationships(); optionallyAddMissingContractualRelationships();
@ -787,7 +809,7 @@ public class ImportOfficeData extends ContextBasedTest {
return containsRole(rec, "partner"); return containsRole(rec, "partner");
} }
private static void addRelationship( private static HsOfficeRelationshipEntity addRelationship(
final HsOfficePersonEntity partnerPerson, final HsOfficePersonEntity partnerPerson,
final HsOfficePersonEntity contactPerson, final HsOfficePersonEntity contactPerson,
final HsOfficeContactEntity contact, final HsOfficeContactEntity contact,
@ -799,6 +821,7 @@ public class ImportOfficeData extends ContextBasedTest {
.relType(representative) .relType(representative)
.build(); .build();
relationships.put(relationshipId++, rel); relationships.put(relationshipId++, rel);
return rel;
} }
private HsOfficePersonEntity initPerson(final HsOfficePersonEntity person, final Record contactRecord) { private HsOfficePersonEntity initPerson(final HsOfficePersonEntity person, final Record contactRecord) {
@ -843,9 +866,9 @@ public class ImportOfficeData extends ContextBasedTest {
return false; return false;
} }
private void verifyContainsOnly(final String roles, final String... allowedRoles) { private void verifyContainsOnlyKnownRoles(final String roles) {
final var allowedRolesSet = stream(KNOWN_ROLES).collect(Collectors.toSet());
final var givenRolesSet = stream(roles.replace(" ", "").split(",")).collect(Collectors.toSet()); final var givenRolesSet = stream(roles.replace(" ", "").split(",")).collect(Collectors.toSet());
final var allowedRolesSet = stream(allowedRoles).collect(Collectors.toSet());
final var unexpectedRolesSet = new HashSet<>(givenRolesSet); final var unexpectedRolesSet = new HashSet<>(givenRolesSet);
unexpectedRolesSet.removeAll(allowedRolesSet); unexpectedRolesSet.removeAll(allowedRolesSet);
assertThat(unexpectedRolesSet).isEmpty(); assertThat(unexpectedRolesSet).isEmpty();

View File

@ -122,7 +122,7 @@ class HsOfficePartnerControllerAcceptanceTest {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike("Third").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -191,8 +191,9 @@ class HsOfficePartnerControllerAcceptanceTest {
void globalAdmin_canNotAddPartner_ifPersonDoesNotExist() { void globalAdmin_canNotAddPartner_ifPersonDoesNotExist() {
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
final var mandantPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPersonUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6"); final var givenPersonUuid = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6");
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("fourth").get(0);
final var location = RestAssured // @formatter:off final var location = RestAssured // @formatter:off
.given() .given()
@ -201,11 +202,22 @@ class HsOfficePartnerControllerAcceptanceTest {
.body(""" .body("""
{ {
"partnerNumber": "12345", "partnerNumber": "12345",
"contactUuid": "%s", "partnerRole": {
"relAnchorUuid": "%s",
"relType": "PARTNER",
"relHolder": "%s",
"contact": "%s",
},
"personUuid": "%s", "personUuid": "%s",
"contactUuid": "%s",
"details": {} "details": {}
} }
""".formatted(givenContact.getUuid(), givenPersonUuid)) """.formatted(
mandantPerson.getUuid(),
givenPersonUuid,
givenContact.getUuid(),
givenPersonUuid,
givenContact.getUuid()))
.port(port) .port(port)
.when() .when()
.post("http://localhost/api/hs/office/partners") .post("http://localhost/api/hs/office/partners")

View File

@ -74,13 +74,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var count = partnerRepo.count(); final var count = partnerRepo.count();
final var givenPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0); final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike("First GmbH").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("first contact").get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
// when // when
final var result = attempt(em, () -> { final var result = attempt(em, () -> {
final var newPartner = toCleanup(HsOfficePartnerEntity.builder() final var newPartner = toCleanup(HsOfficePartnerEntity.builder()
.person(givenPerson) .partnerRole(partnerRole)
.person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder() .details(HsOfficePartnerDetailsEntity.builder()
.build()) .build())
@ -148,11 +158,12 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// relationship - TODO: check and cleanup // relationship - TODO: check and cleanup
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role partner#22222:EBess-4th.tenant by system and assume }",
"{ grant role partner#22222:EBess-4th.agent to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role global#global.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role contact#4th.admin by system and assume }",
"{ grant role person#HostsharingeG.tenant to role person#EBess.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#EBess.admin by system and assume }",
"{ grant role person#EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.owner to role person#HostsharingeG.admin by system and assume }",
"{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }", "{ grant role relationship#HostsharingeG-with-PARTNER-EBess.tenant to role person#HostsharingeG.admin by system and assume }",
"{ grant perm edit on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }", "{ grant perm edit on relationship#HostsharingeG-with-PARTNER-EBess to role relationship#HostsharingeG-with-PARTNER-EBess.admin by system and assume }",
@ -311,6 +322,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth");
final var newPartnerRole = em.createNativeQuery(
"select uuid from hs_office_relationship where uuid=:partnerRoleUuid")
.setParameter("partnerRoleUuid", givenPartner.getPartnerRole().getUuid())
.getSingleResult();
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent"); "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
@ -412,7 +427,8 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
return partnerRepo.deleteByUuid(givenPartner.getUuid()); return relationshipRepo.deleteByUuid(givenPartner.getPartnerRole().getUuid());
// return partnerRepo.deleteByUuid(givenPartner.getUuid());
}); });
// then // then
@ -427,9 +443,8 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_partner'; where targettable = 'hs_office_partner';
"""); """);
@ -455,11 +470,23 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
final Integer partnerNumber, final String person, final String contact) { final Integer partnerNumber, 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(person).get(0); final var givenMandantorPerson = personRepo.findPersonByOptionalNameLike("Hostsharing eG").get(0);
final var givenPartnerPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var partnerRole = HsOfficeRelationshipEntity.builder()
.relHolder(givenPartnerPerson)
.relType(HsOfficeRelationshipType.PARTNER)
.relAnchor(givenMandantorPerson)
.contact(givenContact)
.build();
relationshipRepo.save(partnerRole);
em.flush(); // TODO: why is that necessary?
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.partnerNumber(partnerNumber) .partnerNumber(partnerNumber)
.person(givenPerson) .partnerRole(partnerRole)
.person(givenPartnerPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.build(); .build();

View File

@ -262,9 +262,8 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_person'; where targettable = 'hs_office_person';
"""); """);

View File

@ -19,6 +19,18 @@ class HsOfficeRelationshipEntityUnitTest {
.givenName("Mellie") .givenName("Mellie")
.build(); .build();
@Test
void toStringReturnsAllProperties() {
final var given = HsOfficeRelationshipEntity.builder()
.relType(HsOfficeRelationshipType.SUBSCRIBER)
.relMark("members-announce")
.relAnchor(anchor)
.relHolder(holder)
.build();
assertThat(given.toString()).isEqualTo("rel(relAnchor='LP some trade name', relType='SUBSCRIBER', relMark='members-announce', relHolder='NP Meier, Mellie')");
}
@Test @Test
void toShortString() { void toShortString() {
final var given = HsOfficeRelationshipEntity.builder() final var given = HsOfficeRelationshipEntity.builder()

View File

@ -370,9 +370,8 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_relationship'; where targettable = 'hs_office_relationship';
"""); """);

View File

@ -390,9 +390,8 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest {
public void auditJournalLogIsAvailable() { public void auditJournalLogIsAvailable() {
// given // given
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
select c.currenttask, j.targettable, j.targetop select currentTask, targetTable, targetOp
from tx_journal j from tx_journal_v
join tx_context c on j.contextId = c.contextId
where targettable = 'hs_office_sepamandate'; where targettable = 'hs_office_sepamandate';
"""); """);

View File

@ -6,8 +6,8 @@ contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zip
# eine juristische Person mit drei separaten Ansprechpartnern, vip-contact und ex-partner # eine juristische Person mit drei separaten Ansprechpartnern, vip-contact und ex-partner
1200; 20;; ; ; ; JM e.K.;; Wiesenweg 15; 12335; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; jm-ex-partner@example.org; ex-partner 1200; 20;; ; ; ; JM e.K.;; Wiesenweg 15; 12335; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; jm-ex-partner@example.org; ex-partner
1201; 20; Frau; Jenny; Meyer-Billing; Dr.; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing 1201; 20; Frau; Jenny; Meyer-Billing; Dr.; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing
1202; 20; Herr; Andrew; Meyer-Operation; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation,vip-contact 1202; 20; Herr; Andrew; Meyer-Operation; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation,vip-contact,subscriber:operations-announce
1203; 20; Herr; Philip; Meyer-Contract; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual 1203; 20; Herr; Philip; Meyer-Contract; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual,subscriber:members-announce,subscriber:customers-announce
# eine juristische Person mit nur einem Ansprechpartner und explizitem contractual # eine juristische Person mit nur einem Ansprechpartner und explizitem contractual
1301; 22; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation 1301; 22; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation

1 contact_id; bp_id; salut; first_name; last_name; title; firma; co; street; zipcode;city; country; phone_private; phone_office; phone_mobile; fax; email; roles
6 1201; 20; Frau; Jenny; Meyer-Billing; Dr.; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 7777777; +49 30 1111111; ; +49 30 2222222; jm-billing@example.org; billing
7 1202; 20; Herr; Andrew; Meyer-Operation; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation,vip-contact 1202; 20; Herr; Andrew; Meyer-Operation; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 3333333; ; +49 30 4444444; am-operation@example.org; operation,vip-contact,subscriber:operations-announce
8 1203; 20; Herr; Philip; Meyer-Contract; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual 1203; 20; Herr; Philip; Meyer-Contract; ; JM GmbH;; Waldweg 5; 11001; Berlin; DE; +49 30 6666666; +49 30 5555555; ; +49 30 6666666; pm-partner@example.org; partner,contractual,subscriber:members-announce,subscriber:customers-announce
9 # eine juristische Person mit nur einem Ansprechpartner und explizitem contractual
10 1301; 22; ; Petra; Schmidt; ; Test PS;; ; ; ; ; ; ; ; ; ps@example.com; partner,billing,contractual,operation
11
12
13