Compare commits

..

No commits in common. "368170d27e1a8a5f28c1a82ba5d379dcd087eb3d" and "5280527eae350c0ae0f5b0f688f616b363cdb5b9" have entirely different histories.

45 changed files with 388 additions and 685 deletions

View File

@ -5,6 +5,7 @@ For architecture consider the files in the `doc` and `adr` folder.
<!-- generated TOC begin: -->
- [Setting up the Development Environment](#setting-up-the-development-environment)
- [SDKMAN](#sdkman)
- [PostgreSQL Server](#postgresql-server)
- [Markdown](#markdown)
- [Render Markdown embedded PlantUML](#render-markdown-embedded-plantuml)
@ -50,13 +51,15 @@ Everything is tested on _Ubuntu Linux 22.04_ and _MacOS Monterey (12.4)_.
To be able to build and run the Java Spring Boot application, you need the following tools:
- Docker 20.x (on MacOS you also need *Docker Desktop* or similar) or Podman
- optionally: PostgreSQL Server 15.5-bookworm
- Docker 20.x (on MacOS you also need *Docker Desktop* or similar)
- PostgreSQL Server 15.5-bookworm
(see instructions below to install and run in Docker)
- The matching Java JDK at will be automatically installed by Gradle toolchain support.
- You also might need an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and a GUI Frontend for *PostgreSQL* like *Postbird*.
- Java JDK at least recent enough to run Gradle
(JDK 17.x will be automatically installed by Gradle toolchain support)
If you have at least Docker and the Java JDK installed in appropriate versions and in your `PATH`, then you can start like this:
You also might need an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and a GUI Frontend for *PostgreSQL* like *Postbird*.
If you have at least Docker, the Java JDK and Gradle installed in appropriate versions and in your `PATH`, then you can start like this:
cd your-hsadmin-ng-directory
@ -102,9 +105,30 @@ And to see the full, currently implemented, API, open http://localhost:8080/swag
If you still need to install some of these tools, find some hints in the next chapters.
### SDKMAN
*SdkMan* is not necessary, but helpful to install and switch between different versions of SDKs (Software-Development-Kits) and development tools in general, e.g. *JDK* and *Gradle*.
It is available for _Linux_ and _MacOS_, _WSL_, _Cygwin_, _Solaris_ and _FreeBSD_.
You can get it from: https://sdkman.io/.
<big>**&#9888;**</big>
Yeah, the `curl ... | bash` install method looks quite scary;
but in a development environment you're downloading executables all the time,
e.g. through `npm`, `Maven` or `Gradle` when downloading dependencies.
Thus, maybe you should at least use a separate Linux account for development.
Once it's installed, you can install *JDK* and *Gradle*:
sdk install java 17.0.3-tem
sdk install gradle
sdk use java 17.0.3-tem # use this to switch between installed JDK versions
### PostgreSQL Server
You could use any PostgreSQL Server (version 15) installed on your machine.
You could use any PostgreSQL Server (from version 13 on) installed on your machine.
You might amend the port and user settings in `src/main/resources/application.yml`, though.
But the easiest way to run PostgreSQL is via Docker.
@ -592,16 +616,7 @@ Summary for Debian-based Linux systems:
sudo apt-get -y install podman
```
It is possible to move the storage directory to /tmp, e.g. to increase performance or to avoid issues with NFS mounted home directories:
```shell
cat .config/containers/storage.conf
[storage]
driver = "vfs"
graphRoot = "/tmp/containers/storage"
```
2. Then start it like this:
Then start it like this:
```shell
systemctl --user enable --now podman.socket

View File

@ -207,7 +207,7 @@ project.tasks.spotlessJava.dependsOn(
// OWASP Dependency Security Test
dependencyCheck {
nvd {
apiKey = project.properties['OWASP_API_KEY'] // set it in ~/.gradle/gradle.properties
apiKey = project.property('OWASP_API_KEY') // set it in ~/.gradle/gradle.properties
delay = 16000
}
format = 'ALL'

View File

@ -59,6 +59,9 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
final var saved = bankAccountRepo.save(entityToSave);
// em.persist(entityToSave);
// final var saved = entityToSave;
// em.flush();
final var uri =
MvcUriComponentsBuilder.fromController(getClass())

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.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.repository.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;

View File

@ -3,7 +3,6 @@ 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.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator;
@ -24,7 +23,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopAssetsTransaction")
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, HasUuid {
public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable {
private static Stringify<HsOfficeCoopAssetsTransactionEntity> stringify = stringify(HsOfficeCoopAssetsTransactionEntity.class)
.withProp(e -> e.getMembership().getMemberNumber())

View File

@ -1,12 +1,5 @@
package net.hostsharing.hsadminng.hs.office.coopassets;
public enum HsOfficeCoopAssetsTransactionType {
ADJUSTMENT, // correction of wrong bookings
DEPOSIT, // payment received from member after signing shares, >0
DISBURSAL, // payment send to member after cancellation of shares, <0
TRANSFER, // transferring shares to another member, <0
ADOPTION, // receiving shares from another member, >0
CLEARING, // settlement with members dept, <0
LOSS, // assignment of balance sheet loss in case of cancellation of shares, <0
LIMITATION // limitation period was reached after impossible disbursal, <0
ADJUSTMENT, DEPOSIT, DISBURSAL, TRANSFER, ADOPTION, CLEARING, LOSS
}

View File

@ -3,7 +3,6 @@ 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.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -21,7 +20,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor
@AllArgsConstructor
@DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid {
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withProp(e -> e.getMembership().getMemberNumber())

View File

@ -1,7 +1,5 @@
package net.hostsharing.hsadminng.hs.office.coopshares;
public enum HsOfficeCoopSharesTransactionType {
ADJUSTMENT, // correction of wrong bookings
SUBSCRIPTION, // shares signed, e.g. with the declaration of accession, >0
CANCELLATION; // shares terminated, e.g. when a membership is resigned, <0
ADJUSTMENT, SUBSCRIPTION, CANCELLATION;
}

View File

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

View File

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

View File

@ -58,9 +58,6 @@ 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

@ -3,15 +3,14 @@ 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;
@ -36,9 +35,6 @@ 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;
@ -48,7 +44,7 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
private HsOfficeContactEntity contact;
@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH }, optional = true)
@JoinColumn(name = "detailsuuid")
@JoinColumn(name = "detailsuuid", nullable = true)
@NotFound(action = NotFoundAction.IGNORE)
private HsOfficePartnerDetailsEntity details;
@ -59,6 +55,6 @@ public class HsOfficePartnerEntity implements Stringifyable, HasUuid {
@Override
public String toShortString() {
return Optional.ofNullable(person).map(HsOfficePersonEntity::toShortString).orElse("<person=null>");
return person.toShortString();
}
}

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.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.repository.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type;

View File

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

View File

@ -12,13 +12,8 @@ components:
debitorNumber:
type: integer
format: int32
minimum: 1000000
maximum: 9999999
debitorNumberSuffix:
type: integer
format: int8
minimum: 00
maximum: 99
minimum: 10000
maximum: 99999
partner:
$ref: './hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
billingContact:
@ -32,9 +27,6 @@ components:
type: boolean
refundBankAccount:
$ref: './hs-office-bankaccount-schemas.yaml#/components/schemas/HsOfficeBankAccount'
defaultPrefix:
type: string
pattern: '^[a-z0-9]{3}$'
HsOfficeDebitorPatch:
type: object
@ -57,10 +49,6 @@ components:
type: string
format: uuid
nullable: true
defaultPrefix:
type: string
pattern: '^[a-z0-9]{3}$'
nullable: true
HsOfficeDebitorInsert:
type: object
@ -73,11 +61,11 @@ components:
type: string
format: uuid
nullable: false
debitorNumberSuffix:
debitorNumber:
type: integer
format: int8
minimum: 00
maximum: 99
format: int32
minimum: 10000
maximum: 99999
vatId:
type: string
vatCountryCode:
@ -88,11 +76,6 @@ 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 text;
roleNameParts text;
roleName varchar(63);
roleNameParts varchar(63);
objectTableToAssume varchar(63);
objectNameToAssume varchar(63);
objectUuidToAssume uuid;

View File

@ -31,7 +31,6 @@ 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,6 +27,7 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
language plpgsql
strict as $$
declare
hsOfficePartnerTenant RbacRoleDescriptor;
oldPerson hs_office_person;
newPerson hs_office_person;
oldContact hs_office_contact;
@ -165,8 +166,6 @@ 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,10 +8,7 @@
/*
Creates a single partner test record.
*/
create or replace procedure createHsOfficePartnerTestData(
debitorNumberPrefix numeric(5),
personTradeOrFamilyName varchar,
contactLabel varchar )
create or replace procedure createHsOfficePartnerTestData( personTradeOrFamilyName varchar, contactLabel varchar )
language plpgsql as $$
declare
currentTask varchar;
@ -54,8 +51,8 @@ begin
end if;
insert
into hs_office_partner (uuid, debitorNumberPrefix, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), debitorNumberPrefix, relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
into hs_office_partner (uuid, personuuid, contactuuid, detailsUuid)
values (uuid_generate_v4(), relatedPerson.uuid, relatedContact.uuid, relatedDetailsUuid);
end; $$;
--//
@ -67,11 +64,11 @@ end; $$;
do language plpgsql $$
begin
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');
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');
end;
$$;
--//

View File

@ -8,17 +8,12 @@ create table hs_office_debitor
(
uuid uuid unique references RbacObject (uuid) initially deferred,
partnerUuid uuid not null references hs_office_partner(uuid),
billable boolean not null default true,
debitorNumberSuffix numeric(2) not null,
debitorNumber numeric(5) 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),
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)$'
)
refundBankAccountUuid uuid references hs_office_bankaccount(uuid)
-- TODO.impl: SEPA-mandate
);
--//

View File

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

View File

@ -8,12 +8,7 @@
/*
Creates a single debitor test record.
*/
create or replace procedure createHsOfficeDebitorTestData(
debitorNumberSuffix numeric(5),
partnerTradeName varchar,
billingContactLabel varchar,
defaultPrefix varchar
)
create or replace procedure createHsOfficeDebitorTestData( partnerTradeName varchar, billingContactLabel varchar )
language plpgsql as $$
declare
currentTask varchar;
@ -21,6 +16,7 @@ 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;
@ -32,14 +28,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, relatedPartner.debitorNumberPrefix || '00';
raise notice 'creating test debitor: % (#%)', idName, debitorNumberSuffix;
raise notice 'creating test debitor: % (#%)', idName, newDebitorNumber;
raise notice '- using partner (%): %', relatedPartner.uuid, relatedPartner;
raise notice '- using billingContact (%): %', relatedContact.uuid, relatedContact;
insert
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);
into hs_office_debitor (uuid, partneruuid, debitornumber, billingcontactuuid, vatbusiness, refundbankaccountuuid)
values (uuid_generate_v4(), relatedPartner.uuid, newDebitorNumber, relatedContact.uuid, true, relatedBankAccountUuid);
end; $$;
--//
@ -50,9 +46,9 @@ end; $$;
do language plpgsql $$
begin
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');
call createHsOfficeDebitorTestData('First GmbH', 'first contact');
call createHsOfficeDebitorTestData('Second e.K.', 'second contact');
call createHsOfficeDebitorTestData('Third OHG', 'third contact');
end;
$$;
--//

View File

@ -15,8 +15,7 @@ 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',
membership_fee_billable boolean not null default true
reasonForTermination HsOfficeReasonForTermination not null default 'NONE'
);
--//

View File

@ -27,7 +27,7 @@ create or replace function hsOfficeMembershipRbacRolesTrigger()
language plpgsql
strict as $$
declare
newHsOfficePartner hs_office_partner;
newHsOfficePartner hs_office_partner;
newHsOfficeDebitor hs_office_debitor;
begin
@ -92,8 +92,7 @@ execute procedure hsOfficeMembershipRbacRolesTrigger();
--changeset hs-office-membership-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
call generateRbacIdentityView('hs_office_membership', idNameExpression => $idName$
target.memberNumber ||
':' || (select split_part(idName, ':', 2) from hs_office_partner_iv p where p.uuid = target.partnerUuid)
target.memberNumber || (select idName from hs_office_partner_iv p where p.uuid = target.partnerUuid)
$idName$);
--//
@ -105,8 +104,7 @@ call generateRbacRestrictedView('hs_office_membership',
orderby => 'target.memberNumber',
columnUpdates => $updates$
validity = new.validity,
reasonForTermination = new.reasonForTermination,
membership_fee_billable = new.membership_fee_billable
reasonForTermination = new.reasonForTermination
$updates$);
--//

View File

@ -8,17 +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;
currentTask varchar;
idName varchar;
relatedPartner hs_office_partner;
relatedDebitor hs_office_debitor;
newMemberNumber numeric;
begin
idName := cleanIdentifier( forPartnerTradeName || '#' || forMainDebitorNumber);
currentTask := 'creating Membership test-data ' || idName;
@ -28,10 +25,7 @@ begin
select partner.* from hs_office_partner partner
join hs_office_person person on person.uuid = partner.personUuid
where person.tradeName = forPartnerTradeName into relatedPartner;
-- 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 d.* from hs_office_debitor d where d.debitorNumber = forMainDebitorNumber into relatedDebitor;
select coalesce(max(memberNumber)+1, 10001) from hs_office_membership into newMemberNumber;
raise notice 'creating test Membership: %', idName;
@ -39,7 +33,6 @@ 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; $$;
--//
@ -51,14 +44,9 @@ end; $$;
do language plpgsql $$
begin
call createHsOfficeMembershipTestData('First GmbH', 11);
call createHsOfficeMembershipTestData('Second e.K.', 12);
call createHsOfficeMembershipTestData('Third OHG', 13);
call createHsOfficeMembershipTestData('First GmbH', 10001);
call createHsOfficeMembershipTestData('Second e.K.', 10002);
call createHsOfficeMembershipTestData('Third OHG', 10003);
end;
-- begin
-- call createHsOfficeMembershipTestData('First GmbH', 1000100);
-- call createHsOfficeMembershipTestData('Second e.K.', 1000200);
-- call createHsOfficeMembershipTestData('Third OHG', 1000300);
-- end;
$$;
--//

View File

@ -200,9 +200,7 @@ public class ArchitectureTest {
public static final ArchRule hsOfficeCoopSharesPackageRule = classes()
.that().resideInAPackage("..hs.office.coopshares..")
.should().onlyBeAccessed().byClassesThat()
.resideInAnyPackage(
"..hs.office.coopshares..",
"..hs.office.migration..");
.resideInAnyPackage("..hs.office.coopshares..");
@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,7 +200,8 @@ class HsOfficeCoopAssetsTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopAssetsTransactions() {
// given:
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.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,7 +199,8 @@ class HsOfficeCoopSharesTransactionRepositoryIntegrationTest extends ContextBase
@Test
public void normalUser_canViewOnlyRelatedCoopSharesTransactions() {
// given:
context("superuser-alex@hostsharing.net", "hs_office_partner#10001:FirstGmbH-firstcontact.admin");
context("superuser-alex@hostsharing.net", "hs_office_partner#FirstGmbH-firstcontact.admin");
// "hs_office_person#FirstGmbH.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_SUFFIX = 90;
private static byte nextDebitorSuffix = LOWEST_TEMP_DEBITOR_SUFFIX;
private static final int LOWEST_TEMP_DEBITOR_NUMBER = 20000;
private static int nextDebitorNumber = LOWEST_TEMP_DEBITOR_NUMBER;
@LocalServerPort
private Integer port;
@ -81,8 +81,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
.body("", lenientlyEquals("""
[
{
"debitorNumber": 1000111,
"debitorNumberSuffix": 11,
"debitorNumber": 10001,
"partner": { "person": { "personType": "LEGAL" } },
"billingContact": { "label": "first contact" },
"vatId": null,
@ -91,8 +90,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
"refundBankAccount": { "holder": "First GmbH" }
},
{
"debitorNumber": 1000212,
"debitorNumberSuffix": 12,
"debitorNumber": 10002,
"partner": { "person": { "tradeName": "Second e.K." } },
"billingContact": { "label": "second contact" },
"vatId": null,
@ -101,8 +99,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
"refundBankAccount": { "holder": "Second e.K." }
},
{
"debitorNumber": 1000313,
"debitorNumberSuffix": 13,
"debitorNumber": 10003,
"partner": { "person": { "tradeName": "Third OHG" } },
"billingContact": { "label": "third contact" },
"vatId": null,
@ -123,14 +120,14 @@ class HsOfficeDebitorControllerAcceptanceTest {
.header("current-user", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/debitors?debitorNumber=1000212")
.get("http://localhost/api/hs/office/debitors?debitorNumber=10002")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"debitorNumber": 1000212,
"debitorNumber": 10002,
"partner": { "person": { "tradeName": "Second e.K." } },
"billingContact": { "label": "second contact" },
"vatId": null,
@ -163,14 +160,13 @@ class HsOfficeDebitorControllerAcceptanceTest {
{
"partnerUuid": "%s",
"billingContactUuid": "%s",
"debitorNumberSuffix": "%s",
"debitorNumber": "%s",
"vatId": "VAT123456",
"vatCountryCode": "DE",
"vatBusiness": true,
"refundBankAccountUuid": "%s",
"defaultPrefix": "for"
"refundBankAccountUuid": "%s"
}
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix, givenBankAccount.getUuid()))
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber, givenBankAccount.getUuid()))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@ -179,7 +175,6 @@ 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()))
@ -207,10 +202,9 @@ class HsOfficeDebitorControllerAcceptanceTest {
{
"partnerUuid": "%s",
"billingContactUuid": "%s",
"debitorNumberSuffix": "%s",
"defaultPrefix": "for"
"debitorNumber": "%s"
}
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorSuffix))
""".formatted( givenPartner.getUuid(), givenContact.getUuid(), ++nextDebitorNumber))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@ -224,7 +218,6 @@ 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
@ -249,14 +242,12 @@ class HsOfficeDebitorControllerAcceptanceTest {
{
"partnerUuid": "%s",
"billingContactUuid": "%s",
"debitorNumberSuffix": "%s",
"debitorNumber": "%s",
"vatId": "VAT123456",
"vatCountryCode": "DE",
"vatBusiness": true,
"defaultPrefix": "thi"
"vatBusiness": true
}
"""
.formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorSuffix))
""".formatted( givenPartner.getUuid(), givenContactUuid, ++nextDebitorNumber))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@ -281,13 +272,12 @@ class HsOfficeDebitorControllerAcceptanceTest {
{
"partnerUuid": "%s",
"billingContactUuid": "%s",
"debitorNumberSuffix": "%s",
"debitorNumber": "%s",
"vatId": "VAT123456",
"vatCountryCode": "DE",
"vatBusiness": true,
"defaultPrefix": "for"
"vatBusiness": true
}
""".formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorSuffix))
""".formatted( givenPartnerUuid, givenContact.getUuid(), ++nextDebitorNumber))
.port(port)
.when()
.post("http://localhost/api/hs/office/debitors")
@ -385,8 +375,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
"contactUuid": "%s",
"vatId": "VAT222222",
"vatCountryCode": "AA",
"vatBusiness": true,
"defaultPrefix": "for"
"vatBusiness": true
}
""".formatted(givenContact.getUuid()))
.port(port)
@ -399,7 +388,6 @@ 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
@ -534,10 +522,9 @@ class HsOfficeDebitorControllerAcceptanceTest {
final var givenPartner = partnerRepo.findPartnerByOptionalNameLike("Fourth").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0);
final var newDebitor = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix(++nextDebitorSuffix)
.debitorNumber(++nextDebitorNumber)
.partner(givenPartner)
.billingContact(givenContact)
.defaultPrefix("abc")
.build();
return debitorRepo.save(newDebitor);
@ -550,7 +537,7 @@ class HsOfficeDebitorControllerAcceptanceTest {
jpaAttempt.transacted(() -> {
context.define("superuser-alex@hostsharing.net");
final var count = em.createQuery(
"DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumberSuffix >= " + LOWEST_TEMP_DEBITOR_SUFFIX)
"DELETE FROM HsOfficeDebitorEntity d WHERE d.debitorNumber > " + LOWEST_TEMP_DEBITOR_NUMBER)
.executeUpdate();
System.out.printf("deleted %d entities%n", count);
});

View File

@ -31,7 +31,6 @@ 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;
@ -63,7 +62,6 @@ class HsOfficeDebitorEntityPatcherUnitTest extends PatchUnitTestBase<
entity.setVatId("initial VAT-ID");
entity.setVatCountryCode("AA");
entity.setVatBusiness(true);
entity.setDefaultPrefix("abc");
return entity;
}
@ -102,12 +100,6 @@ 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,50 +13,30 @@ class HsOfficeDebitorEntityUnitTest {
@Test
void toStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder()
.debitorNumberSuffix((byte)67)
.debitorNumber(123456)
.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(1234567: some trade name)");
assertThat(result).isEqualTo("debitor(123456: some trade name)");
}
@Test
void toStringWithoutPersonContainsDebitorNumber() {
void toShortStringContainsPartnerAndContact() {
final var given = HsOfficeDebitorEntity.builder()
.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)
.debitorNumber(123456)
.build();
final var result = given.toShortString();
assertThat(result).isEqualTo("1234567");
assertThat(result).isEqualTo("123456");
}
}

View File

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

View File

@ -9,10 +9,8 @@ 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()
.debitorNumberSuffix(DEFAULT_DEBITOR_SUFFIX)
.debitorNumber(10001)
.partner(TEST_PARTNER)
.billingContact(TEST_CONTACT)
.build();

View File

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

View File

@ -27,8 +27,7 @@ class HsOfficeMembershipEntityUnitTest {
void toStringContainsAllProps() {
final var result = givenMembership.toString();
assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))");
// assertThat(result).isEqualTo("Membership(10001, Test Ltd., 1000100, [2020-01-01,))");
assertThat(result).isEqualTo("Membership(10001, Test Ltd., 10001, [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#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"));
"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"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("GmbH-firstcontact", ""))
.map(s -> s.replace("hs_office_", ""))
@ -131,36 +131,32 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
initialGrantNames,
// owner
"{ 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 }",
"{ 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 }",
// admin
"{ 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 }",
"{ 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 }",
// agent
"{ 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 }",
"{ 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 }",
// tenant
"{ 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 }",
"{ 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 }",
// guest
"{ 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 }",
"{ 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 }",
null));
}
@ -185,9 +181,9 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
exactlyTheseMembershipsAreReturned(
result,
"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)");
"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)");
}
@Test
@ -202,7 +198,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
null);
// then
exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 1000111, [2022-10-01,), NONE)");
exactlyTheseMembershipsAreReturned(result, "Membership(10001, First GmbH, 10001, [2022-10-01,), NONE)");
}
@Test
@ -214,7 +210,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var result = membershipRepo.findMembershipsByOptionalPartnerUuidAndOptionalMemberNumber(null, 10002);
// then
exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 1000212, [2022-10-01,), NONE)");
exactlyTheseMembershipsAreReturned(result, "Membership(10002, Second e.K., 10002, [2022-10-01,), NONE)");
}
}
@ -228,7 +224,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var givenMembership = givenSomeTemporaryMembership("First", "First");
assertThatMembershipIsVisibleForUserWithRole(
givenMembership,
"hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
"hs_office_debitor#10001FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now();
@ -255,13 +251,13 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
final var givenMembership = givenSomeTemporaryMembership("First", "First");
assertThatMembershipIsVisibleForUserWithRole(
givenMembership,
"hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
"hs_office_debitor#10001FirstGmbH-firstcontact.admin");
assertThatMembershipExistsAndIsAccessibleToCurrentContext(givenMembership);
final var newValidityEnd = LocalDate.now();
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_debitor#1000111:FirstGmbH-firstcontact.admin");
context("superuser-alex@hostsharing.net", "hs_office_debitor#10001FirstGmbH-firstcontact.admin");
givenMembership.setValidity(Range.closedOpen(
givenMembership.getValidity().lower(), newValidityEnd));
return membershipRepo.save(givenMembership);
@ -329,7 +325,7 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_debitor#1000313:ThirdOHG-thirdcontact.admin");
context("superuser-alex@hostsharing.net", "hs_office_debitor#10003ThirdOHG-thirdcontact.admin");
assertThat(membershipRepo.findByUuid(givenMembership.getUuid())).isPresent();
membershipRepo.deleteByUuid(givenMembership.getUuid());
@ -386,8 +382,8 @@ class HsOfficeMembershipRepositoryIntegrationTest extends ContextBasedTest {
// then
assertThat(customerLogEntries).map(Arrays::toString).contains(
"[creating Membership test-data FirstGmbH11, hs_office_membership, INSERT]",
"[creating Membership test-data Seconde.K.12, hs_office_membership, INSERT]");
"[creating Membership test-data FirstGmbH10001, hs_office_membership, INSERT]",
"[creating Membership test-data Seconde.K.10002, hs_office_membership, INSERT]");
}
@BeforeEach

View File

@ -7,10 +7,6 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity;
import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionType;
import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionEntity;
import net.hostsharing.hsadminng.hs.office.coopshares.HsOfficeCoopSharesTransactionType;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination;
@ -19,14 +15,23 @@ 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.*;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.Commit;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.ReflectionUtils;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
@ -35,11 +40,12 @@ import jakarta.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.stream;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
@ -51,26 +57,23 @@ 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 verify the data conversion.
* There is some test data in Java resources to verfiy 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)
public class ImportOfficeTables extends ContextBasedTest {
private static NavigableMap<Integer, HsOfficeContactEntity> contacts = new TreeMap<>();
private static NavigableMap<Integer, HsOfficePersonEntity> persons = new TreeMap<>();
private static NavigableMap<Integer, HsOfficePartnerEntity> partners = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeDebitorEntity> debitors = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeMembershipEntity> memberships = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeSepaMandateEntity> sepaMandates = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeBankAccountEntity> bankAccounts = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeCoopAssetsTransactionEntity> coopAssets = new TreeMap<>();
private static Map<Integer, HsOfficeContactEntity> contacts = new HashMap<>();
private static Map<Integer, HsOfficePersonEntity> persons = new HashMap<>();
private static Map<Integer, HsOfficePartnerEntity> partners = new HashMap<>();
private static Map<Integer, HsOfficeDebitorEntity> debitors = new HashMap<>();
private static Map<Integer, HsOfficeMembershipEntity> memberships = new HashMap<>();
private static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new HashMap<>();
private static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new HashMap<>();
@PersistenceContext
EntityManager em;
@ -139,9 +142,9 @@ public class ImportOfficeTables extends ContextBasedTest {
""");
assertThat(contacts.toString()).isEqualToIgnoringWhitespace("""
{
71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'),
101=contact(label='Frau Dr. Jenny Meyer , JM e.K.', emailAddresses='jm@example.org'),
102=contact(label='Herr Andrew Meyer , JM e.K.', emailAddresses='am@example.org'),
71=contact(label='Herr Michael Mellies ', emailAddresses='mih@example.org'),
121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com')
}
""");
@ -193,52 +196,6 @@ public class ImportOfficeTables extends ContextBasedTest {
""");
}
@Test
@Order(4)
void importCoopShares() {
try (Reader reader = resourceReader("migration/share-transactions.csv")) {
final var lines = readAllLines(reader);
importCoopShares(justHeader(lines), withoutHeader(lines));
} catch (Exception e) {
throw new RuntimeException(e);
}
assertThat(coopShares.toString()).isEqualToIgnoringWhitespace("""
{
33443=CoopShareTransaction(10007, 2000-12-06, SUBSCRIPTION, 20, initial share subscription),
33451=CoopShareTransaction(10010, 2000-12-06, SUBSCRIPTION, 2, initial share subscription),
33701=CoopShareTransaction(10007, 2005-01-10, SUBSCRIPTION, 40, increase),
33810=CoopShareTransaction(10010, 2016-12-31, CANCELLATION, 22, membership ended)
}
""");
}
@Test
@Order(5)
void importCoopAssets() {
try (Reader reader = resourceReader("migration/asset-transactions.csv")) {
final var lines = readAllLines(reader);
importCoopAssets(justHeader(lines), withoutHeader(lines));
} catch (Exception e) {
throw new RuntimeException(e);
}
assertThat(coopAssets.toString()).isEqualToIgnoringWhitespace("""
{
30000=CoopAssetsTransaction(10007, 2000-12-06, DEPOSIT, 1280.00, for subscription A),
31000=CoopAssetsTransaction(10010, 2000-12-06, DEPOSIT, 128.00, for subscription B),
32000=CoopAssetsTransaction(10007, 2005-01-10, DEPOSIT, 2560.00, for subscription C),
33001=CoopAssetsTransaction(10007, 2005-01-10, TRANSFER, -512.00, for transfer to 10),
33002=CoopAssetsTransaction(10010, 2005-01-10, ADOPTION, 512.00, for transfer from 7),
34001=CoopAssetsTransaction(10010, 2016-12-31, CLEARING, -8.00, for cancellation D),
34002=CoopAssetsTransaction(10010, 2016-12-31, DISBURSAL, -100.00, for cancellation D),
34003=CoopAssetsTransaction(10010, 2016-12-31, LOSS, -20.00, for cancellation D)
}
""");
}
@Test
@Order(10)
@Commit
@ -262,11 +219,7 @@ public class ImportOfficeTables extends ContextBasedTest {
sepaMandates.forEach((id, mandate) -> em.persist(mandate));
updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id");
coopShares.forEach((id, shareTransaction) -> em.persist(shareTransaction));
updateLegacyIds(coopShares, "hs_office_coopsharestransaction_legacy_id", "member_share_id");
coopAssets.forEach((id, assetTransaction) -> em.persist(assetTransaction));
updateLegacyIds(coopShares, "hs_office_coopassetstransaction_legacy_id", "member_asset_id");
// TODO: coopshares+coopassets
em.flush();
}
@ -310,117 +263,49 @@ public class ImportOfficeTables extends ContextBasedTest {
final var person = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.UNKNOWN) // TODO
.build();
persons.put(rec.getInteger("bp_id"), person);
persons.put(toInt(rec.get("bp_id")), person);
final var partner = HsOfficePartnerEntity.builder()
.details(HsOfficePartnerDetailsEntity.builder().build())
.contact(HsOfficeContactEntity.builder().build())
.person(person)
.build();
partners.put(rec.getInteger("bp_id"), partner);
partners.put(toInt(rec.get("bp_id")), partner);
final var debitor = HsOfficeDebitorEntity.builder()
.partner(partner)
// .debitorNumberSuffix(rec.getByte("member_id"))
// .defaultPrefix(rec.getString("member_code"))
.debitorNumber(toInt(rec.get("member_id")))
// .memberCode(rec.get("member_code")) TODO
.partner(partner)
.billingContact(partner.getContact())
.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"))
// .memberRoles(toBool(rec.get("member_role")) TODO
// .authorContract(toBool(rec.get("author_contract")) TODO
// .nonDisclosureContract(toBool(rec.get("nondisc_contract")) TODO
// .free(toBool(rec.get("free")) TODO
// .vatExempt(toBool(rec.get("exempt_vat")) TODO
.vatBusiness("GROSS".equals(rec.get("indicator_vat")))
.vatId(rec.get("uid_vat"))
.build();
debitors.put(rec.getInteger("bp_id"), debitor);
debitors.put(toInt(rec.get("bp_id")), debitor);
partners.put(rec.getInteger("bp_id"), partner);
partners.put(toInt(rec.get("bp_id")), partner);
if (isNotBlank(rec.getString("member_since"))) {
if (isNotBlank(rec.get("member_since"))) {
final var membership = HsOfficeMembershipEntity.builder()
.partner(partner)
.memberNumber(rec.getInteger("member_id"))
.validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until")))
.membershipFeeBillable(rec.isEmpty("member_role"))
.memberNumber(toInt(rec.get("member_id")))
.validity(toPostgresDateRange(localDate(rec.get("member_since")), localDate(rec.get("member_until"))))
.reasonForTermination(
isBlank(rec.getString("member_until"))
isBlank(rec.get("member_until"))
? HsOfficeReasonForTermination.NONE
: HsOfficeReasonForTermination.UNKNOWN)
: HsOfficeReasonForTermination.UNKNOWN) // TODO
.mainDebitor(debitor)
.build();
memberships.put(rec.getInteger("bp_id"), membership);
memberships.put(toInt(rec.get("bp_id")), membership);
}
});
}
private void importCoopShares(final String[] header, final List<String[]> records) {
final var columns = new Columns(header);
records.stream()
.map(this::trimAll)
.map(row -> new Record(columns, row))
.forEach(rec -> {
final var member = memberships.get(rec.getInteger("bp_id"));
final var shareTransaction = HsOfficeCoopSharesTransactionEntity.builder()
.membership(member)
.valueDate(rec.getLocalDate("date"))
.transactionType(
"SUBSCRIPTION".equals(rec.getString("action"))
? HsOfficeCoopSharesTransactionType.SUBSCRIPTION
: "UNSUBSCRIPTION".equals(rec.getString("action"))
? HsOfficeCoopSharesTransactionType.CANCELLATION
: HsOfficeCoopSharesTransactionType.ADJUSTMENT
)
.shareCount(rec.getInteger("quantity"))
.reference(rec.getString("comment"))
.build();
coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
});
}
private void importCoopAssets(final String[] header, final List<String[]> records) {
final var columns = new Columns(header);
records.stream()
.map(this::trimAll)
.map(row -> new Record(columns, row))
.forEach(rec -> {
final var member = memberships.get(rec.getInteger("bp_id"));
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
{
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
put("CLEARING", HsOfficeCoopAssetsTransactionType.CLEARING);
put("PRESCRIPTION", HsOfficeCoopAssetsTransactionType.LIMITATION);
put("PAYBACK", HsOfficeCoopAssetsTransactionType.DISBURSAL);
put("PAYMENT", HsOfficeCoopAssetsTransactionType.DEPOSIT);
}
public HsOfficeCoopAssetsTransactionType get(final String key) {
final var value = super.get(key);
if ( value != null ) {
return value;
}
throw new IllegalStateException("no mapping value found for: " + key);
}
};
final var assetTransaction = HsOfficeCoopAssetsTransactionEntity.builder()
.membership(member)
.valueDate(rec.getLocalDate("date"))
.transactionType(assetTypeMapping.get(rec.getString("action")))
.assetValue(rec.getBigDecimal("amount"))
.reference(rec.getString("comment"))
.build();
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
});
}
private void importSepaMandates(final String[] header, final List<String[]> records) {
final var columns = new Columns(header);
@ -429,25 +314,25 @@ public class ImportOfficeTables extends ContextBasedTest {
.map(this::trimAll)
.map(row -> new Record(columns, row))
.forEach(rec -> {
final var debitor = debitors.get(rec.getInteger("bp_id"));
final var debitor = debitors.get(toInt(rec.get("bp_id")));
final var sepaMandate = HsOfficeSepaMandateEntity.builder()
.debitor(debitor)
.bankAccount(HsOfficeBankAccountEntity.builder()
.holder(rec.getString("bank_customer"))
// .bankName(rec.get("bank_name")) // not supported
.iban(rec.getString("bank_iban"))
.bic(rec.getString("bank_bic"))
.holder(rec.get("bank_customer"))
// .bankName(rec.get("bank_name")) // TODO
.iban(rec.get("bank_iban"))
.bic(rec.get("bank_bic"))
.build())
.reference(rec.getString("mandat_ref"))
.agreement(LocalDate.parse(rec.getString("mandat_signed")))
.reference(rec.get("mandat_ref"))
.agreement(LocalDate.parse(rec.get("mandat_signed")))
.validity(toPostgresDateRange(
rec.getLocalDate("mandat_since"),
rec.getLocalDate("mandat_until")))
toLocalDate(rec.get("mandat_since")),
toLocalDate(rec.get("mandat_until"))))
.build();
sepaMandates.put(rec.getInteger("sepa_mandat_id"), sepaMandate);
bankAccounts.put(rec.getInteger("sepa_mandat_id"), sepaMandate.getBankAccount());
sepaMandates.put(toInt(rec.get("sepa_mandat_id")), sepaMandate);
bankAccounts.put(toInt(rec.get("sepa_mandat_id")), sepaMandate.getBankAccount());
});
}
@ -459,15 +344,15 @@ public class ImportOfficeTables extends ContextBasedTest {
.map(this::trimAll)
.map(row -> new Record(columns, row))
.forEach(rec -> {
if (isNotBlank(rec.getString("roles")) && rec.getString("roles").contains("billing")) {
if (isNotBlank(rec.get("roles")) && rec.get("roles").contains("billing")) {
final var partner = partners.get(rec.getInteger("bp_id"));
final var partner = partners.get(toInt(rec.get("bp_id")));
final var person = partner.getPerson();
person.setTradeName(rec.getString("firma"));
// TODO: title+salutation: add to person
person.setGivenName(rec.getString("first_name"));
person.setFamilyName(rec.getString("last_name"));
person.setTradeName(rec.get("firma"));
// TODO: title+salutation
person.setGivenName(rec.get("first_name"));
person.setFamilyName(rec.get("last_name"));
initContact(partner.getContact(), rec);
@ -479,10 +364,10 @@ public class ImportOfficeTables extends ContextBasedTest {
}
private void initContact(final HsOfficeContactEntity contact, final Record rec) {
contacts.put(rec.getInteger("contact_id"), contact);
contacts.put(toInt(rec.get("contact_id")), contact);
contact.setLabel(toLabel(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"), rec.getString("firma")));
contact.setEmailAddresses(rec.getString("email"));
contact.setLabel(toLabel(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name"), rec.get("firma")));
contact.setEmailAddresses(rec.get("email"));
contact.setPostalAddress(toAddress(rec));
contact.setPhoneNumbers(toPhoneNumbers(rec));
}
@ -497,28 +382,28 @@ public class ImportOfficeTables extends ContextBasedTest {
}
private String toPhoneNumbers(final Record rec) {
final var result = new StringBuilder("{\n");
if (isNotBlank(rec.getString("phone_private")))
result.append(" \"private\": " + "\"" + rec.getString("phone_private") + "\",\n");
if (isNotBlank(rec.getString("phone_office")))
result.append(" \"office\": " + "\"" + rec.getString("phone_office") + "\",\n");
if (isNotBlank(rec.getString("phone_mobile")))
result.append(" \"mobile\": " + "\"" + rec.getString("phone_mobile") + "\",\n");
if (isNotBlank(rec.getString("fax")))
result.append(" \"fax\": " + "\"" + rec.getString("fax") + "\",\n");
if (isNotBlank(rec.get("phone_private")))
result.append(" \"private\": " + "\"" + rec.get("phone_private") + "\",\n");
if (isNotBlank(rec.get("phone_office")))
result.append(" \"office\": " + "\"" + rec.get("phone_office") + "\",\n");
if (isNotBlank(rec.get("phone_mobile")))
result.append(" \"mobile\": " + "\"" + rec.get("phone_mobile") + "\",\n");
if (isNotBlank(rec.get("fax")))
result.append(" \"fax\": " + "\"" + rec.get("fax") + "\",\n");
return (result + "}").replace("\",\n}", "\"\n}");
}
private String toAddress(final Record rec) {
final var result = new StringBuilder();
final var name = toName(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"));
final var name = toName(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name"));
if (isNotBlank(name))
result.append(name + "\n");
if (isNotBlank(rec.getString("firma")))
result.append(rec.getString("firma") + "\n");
if (isNotBlank(rec.getString("co")))
result.append("c/o " + rec.getString("co") + "\n");
if (isNotBlank(rec.getString("street")))
result.append(rec.getString("street") + "\n");
if (isNotBlank(rec.get("firma")))
result.append(rec.get("firma") + "\n");
if (isNotBlank(rec.get("co")))
result.append("c/o " + rec.get("co") + "\n");
if (isNotBlank(rec.get("street")))
result.append(rec.get("street") + "\n");
final var zipcodeAndCity = toZipcodeAndCity(rec);
if (isNotBlank(zipcodeAndCity))
result.append(zipcodeAndCity + "\n");
@ -527,12 +412,12 @@ public class ImportOfficeTables extends ContextBasedTest {
private String toZipcodeAndCity(final Record rec) {
final var result = new StringBuilder();
if (isNotBlank(rec.getString("country")))
result.append(rec.getString("country") + " ");
if (isNotBlank(rec.getString("zipcode")))
result.append(rec.getString("zipcode") + " ");
if (isNotBlank(rec.getString("city")))
result.append(rec.getString("city"));
if (isNotBlank(rec.get("country")))
result.append(rec.get("country") + " ");
if (isNotBlank(rec.get("zipcode")))
result.append(rec.get("zipcode") + " ");
if (isNotBlank(rec.get("city")))
result.append(rec.get("city"));
return result.toString();
}
@ -561,6 +446,23 @@ public class ImportOfficeTables extends ContextBasedTest {
return toLabel(salut, title, firstname, lastname, null);
}
private LocalDate toLocalDate(final String dateString) {
if (isNotBlank(dateString)) {
return LocalDate.parse(dateString);
}
return null;
}
private static Integer toInt(final String value) {
return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0;
}
private LocalDate localDate(final String dateStringNullOrBlank) {
if (isNotBlank(dateStringNullOrBlank)) {
return LocalDate.parse(dateStringNullOrBlank);
}
return null;
}
private Reader resourceReader(@NotNull final String resourcePath) {
return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath));
@ -610,38 +512,7 @@ class Record {
this.row = row;
}
String getString(final String columnName) {
String get(final String columnName) {
return row[columns.indexOf(columnName)];
}
boolean isEmpty(final String columnName) {
final String value = getString(columnName);
return value == null || value.isBlank();
}
Byte getByte(final String columnName) {
final String value = getString(columnName);
return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0;
}
Integer getInteger(final String columnName) {
final String value = getString(columnName);
return isNotBlank(value) ? Integer.valueOf(value.trim()) : 0;
}
BigDecimal getBigDecimal(final String columnName) {
final String value = getString(columnName);
if (isNotBlank(value)) {
return new BigDecimal(value);
}
return null;
}
LocalDate getLocalDate(final String columnName) {
final String dateString = getString(columnName);
if (isNotBlank(dateString)) {
return LocalDate.parse(dateString);
}
return null;
}
}

View File

@ -105,7 +105,6 @@ 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())
@ -116,11 +115,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// then
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames,
"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"));
"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"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("forthcontact", "4th"))
@ -128,31 +127,31 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// owner
"{ 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 }",
"{ 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 }",
// admin
"{ 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 }",
"{ 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 }",
// agent
"{ 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 }",
"{ 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 }",
// tenant
"{ 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 }",
"{ 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 }",
// guest
"{ 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 }",
"{ 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 }",
null));
}
@ -218,10 +217,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() {
// given
context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "fifth contact");
final var givenPartner = givenSomeTemporaryPartnerBessler("fifth contact");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
"hs_office_partner#22222:ErbenBesslerMelBessler-fifthcontact.admin");
"hs_office_person#ErbenBesslerMelBessler.admin");
assertThatPartnerActuallyInDatabase(givenPartner);
context("superuser-alex@hostsharing.net");
final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0);
@ -254,16 +253,16 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void partnerAgent_canNotUpdateRelatedPartner() {
// given
context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth");
final var givenPartner = givenSomeTemporaryPartnerBessler("ninth");
assertThatPartnerIsVisibleForUserWithRole(
givenPartner,
"hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
"hs_office_partner#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#22222:ErbenBesslerMelBessler-ninthcontact.agent");
context("superuser-alex@hostsharing.net", "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent");
givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner);
});
@ -305,7 +304,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() {
// given
context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "tenth");
final var givenPartner = givenSomeTemporaryPartnerBessler("tenth");
// when
final var result = jpaAttempt.transacted(() -> {
@ -325,7 +324,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() {
// given
context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "eleventh");
final var givenPartner = givenSomeTemporaryPartnerBessler("eleventh");
// when
final var result = jpaAttempt.transacted(() -> {
@ -351,7 +350,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(22222, "Erben Bessler", "twelfth");
final var givenPartner = givenSomeTemporaryPartnerBessler("twelfth");
// when
final var result = jpaAttempt.transacted(() -> {
@ -395,14 +394,12 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
});
}
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(
final Integer debitorNumberPrefix, final String person, final String contact) {
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final String contact) {
return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").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,11 +8,10 @@ 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": 1000212,
"debitorNumber": 10002,
"billingContact": { "label": "second contact" }
},
"bankAccount": { "holder": "Second e.K." },
@ -90,7 +90,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
},
{
"debitor": {
"debitorNumber": 1000111,
"debitorNumber": 10001,
"billingContact": { "label": "first contact" }
},
"bankAccount": { "holder": "First GmbH" },
@ -100,7 +100,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
},
{
"debitor": {
"debitorNumber": 1000313,
"debitorNumber": 10003,
"billingContact": { "label": "third contact" }
},
"bankAccount": { "holder": "Third OHG" },
@ -269,7 +269,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
.body("", lenientlyEquals("""
{
"debitor": {
"debitorNumber": 1000111,
"debitorNumber": 10001,
"billingContact": { "label": "first contact" }
},
"bankAccount": {
@ -321,7 +321,7 @@ class HsOfficeSepaMandateControllerAcceptanceTest {
.body("", lenientlyEquals("""
{
"debitor": {
"debitorNumber": 1000111,
"debitorNumber": 10001,
"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(1000111: First GmbH)");
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: 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(1000111: First GmbH)");
assertThat(mandate.getDebitor().toString()).isEqualTo("debitor(10001: 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

@ -134,24 +134,24 @@ class HsOfficeSepaMandateRepositoryIntegrationTest extends ContextBasedTest {
initialGrantNames,
// owner
"{ grant perm * on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
"{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }",
"{ grant perm * on sepamandate#temprefB to role sepamandate#temprefB.owner by system and assume }",
"{ grant role sepamandate#temprefB.owner to role global#global.admin by system and assume }",
// admin
"{ grant perm edit on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
"{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }",
"{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }",
"{ grant perm edit on sepamandate#temprefB to role sepamandate#temprefB.admin by system and assume }",
"{ grant role sepamandate#temprefB.admin to role sepamandate#temprefB.owner by system and assume }",
"{ grant role bankaccount#Paul....tenant to role sepamandate#temprefB.admin by system and assume }",
// agent
"{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin 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#1000111:FirstGmbH-....admin by system and assume }",
"{ grant role sepamandate#temprefB.agent to role sepamandate#temprefB.admin by system and assume }",
"{ grant role debitor#10001FirstGmbH-....tenant to role sepamandate#temprefB.agent by system and assume }",
"{ grant role 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 }",
// tenant
"{ grant role sepamandate#temprefB.tenant to role sepamandate#temprefB.agent by system and assume }",
"{ grant role debitor#1000111:FirstGmbH-....guest to role sepamandate#temprefB.tenant by system and assume }",
"{ grant role bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
"{ grant role 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 bankaccount#Paul....guest to role sepamandate#temprefB.tenant by system and assume }",
// guest
"{ grant perm view on sepamandate#temprefB to role sepamandate#temprefB.guest by system and assume }",

View File

@ -1,9 +1,5 @@
member_asset_id; bp_id; date; action; amount; comment
30000; 7; 2000-12-06; PAYMENT; 1280.00; for subscription A
31000; 10; 2000-12-06; PAYMENT; 128.00; for subscription B
32000; 7; 2005-01-10; PAYMENT; 2560.00; for subscription C
33001; 7; 2005-01-10; HANDOVER; -512.00; for transfer to 10
33002; 10; 2005-01-10; ADOPTION; 512.00; for transfer from 7
34001; 10; 2016-12-31; CLEARING; -8.00; for cancellation D
34002; 10; 2016-12-31; PAYBACK; -100.00; for cancellation D
34003; 10; 2016-12-31; LOSS; -20.00; for cancellation D
member_asset_id; bp_id; date; action; amount
33443; 7; 2000-12-06; PAYMENT; 1280
33451; 10; 2000-12-06; PAYMENT; 128
33701; 7; 2005-01-10; PAYMENT; 2560
33810; 10; 2016-12-31; PAYBACK; 128

1 member_asset_id bp_id date action amount comment
2 30000 33443 7 2000-12-06 PAYMENT 1280.00 1280 for subscription A
3 31000 33451 10 2000-12-06 PAYMENT 128.00 128 for subscription B
4 32000 33701 7 2005-01-10 PAYMENT 2560.00 2560 for subscription C
5 33001 33810 7 10 2005-01-10 2016-12-31 HANDOVER PAYBACK -512.00 128 for transfer to 10
33002 10 2005-01-10 ADOPTION 512.00 for transfer from 7
34001 10 2016-12-31 CLEARING -8.00 for cancellation D
34002 10 2016-12-31 PAYBACK -100.00 for cancellation D
34003 10 2016-12-31 LOSS -20.00 for cancellation D

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;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;
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;

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