Compare commits

..

17 Commits

Author SHA1 Message Date
Michael Hoennig
368170d27e Merge remote-tracking branch 'origin/master' into db-migration 2024-01-10 17:51:02 +01:00
Michael Hoennig
980524e7ab add: membership-fee-billable, debitor.billable, debitor.billingContact, debitor.defaultPrefix 2024-01-10 17:38:11 +01:00
e427bb1784 Merge pull request 'fix+improve-project-setup' (#8) from fix+improve-project-setup into master
Reviewed-on: #8
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
2024-01-09 09:01:12 +01:00
Timotheus Pokorra
238e413aa7 Merge branch 'master' into fix+improve-project-setup 2024-01-09 09:00:26 +01:00
5f9a6d53d8 Merge pull request 'Add information related to Podman storage to README.' (#3) from readme-podman-storage into master
Reviewed-on: #3
Reviewed-by: Michael Hoennig <michael.hoennig@hostsharing.net>
2024-01-09 08:58:57 +01:00
Timotheus Pokorra
fef9fba073 Merge branch 'master' into readme-podman-storage 2024-01-09 08:58:00 +01:00
3d77dcc2ce Merge pull request 'Requirements aktualisiert' (#4) from TP-20240104-readme_requirements into master
Reviewed-on: #4
Reviewed-by: Michael Hoennig <michael.hoennig@hostsharing.net>
2024-01-09 08:56:18 +01:00
Timotheus Pokorra
38e4b00107 Merge branch 'master' into TP-20240104-readme_requirements 2024-01-09 08:55:29 +01:00
Michael Hoennig
7e31e95d57 persist shares+assets 2024-01-08 13:48:31 +01:00
Michael Hoennig
53d46da49a import coop assets 2024-01-08 13:13:24 +01:00
Michael Hoennig
b0bfb127b6 implement import of coop-share-transactions 2024-01-08 11:36:47 +01:00
Michael Hoennig
96ef490207 code cleanup, removing commented code 2024-01-06 18:23:13 +01:00
Michael Hoennig
0a996a9a8f remove SDKMAN from README, JDK is now downloaded by Gradle Toolchain 2024-01-06 18:12:25 +01:00
Michael Hoennig
242b6f88c9 make OWASP_API_KEY optionally 2024-01-06 18:11:24 +01:00
63b02ff9cb Small fix in README 2024-01-04 22:46:40 +01:00
845857c14d Requirements aktualisiert 2024-01-04 09:13:27 +01:00
Michael Hierweck
cec31055a5 Add information related to Podman storage to README. 2024-01-02 12:20:19 +01:00
45 changed files with 685 additions and 388 deletions

View File

@ -5,7 +5,6 @@ For architecture consider the files in the `doc` and `adr` folder.
<!-- generated TOC begin: --> <!-- generated TOC begin: -->
- [Setting up the Development Environment](#setting-up-the-development-environment) - [Setting up the Development Environment](#setting-up-the-development-environment)
- [SDKMAN](#sdkman)
- [PostgreSQL Server](#postgresql-server) - [PostgreSQL Server](#postgresql-server)
- [Markdown](#markdown) - [Markdown](#markdown)
- [Render Markdown embedded PlantUML](#render-markdown-embedded-plantuml) - [Render Markdown embedded PlantUML](#render-markdown-embedded-plantuml)
@ -51,15 +50,13 @@ 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: 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) - Docker 20.x (on MacOS you also need *Docker Desktop* or similar) or Podman
- PostgreSQL Server 15.5-bookworm - optionally: PostgreSQL Server 15.5-bookworm
(see instructions below to install and run in Docker) (see instructions below to install and run in Docker)
- Java JDK at least recent enough to run Gradle - The matching Java JDK at will be automatically installed by Gradle toolchain support.
(JDK 17.x 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*.
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 and the Java JDK installed in appropriate versions and in your `PATH`, then you can start like this:
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 cd your-hsadmin-ng-directory
@ -105,30 +102,9 @@ 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. 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 ### PostgreSQL Server
You could use any PostgreSQL Server (from version 13 on) installed on your machine. You could use any PostgreSQL Server (version 15) installed on your machine.
You might amend the port and user settings in `src/main/resources/application.yml`, though. 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. But the easiest way to run PostgreSQL is via Docker.
@ -616,7 +592,16 @@ Summary for Debian-based Linux systems:
sudo apt-get -y install podman sudo apt-get -y install podman
``` ```
Then start it like this: 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:
```shell ```shell
systemctl --user enable --now podman.socket systemctl --user enable --now podman.socket

View File

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

View File

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

View File

@ -3,7 +3,7 @@ package net.hostsharing.hsadminng.hs.office.contact;
import lombok.*; import lombok.*;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;

View File

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

View File

@ -1,5 +1,12 @@
package net.hostsharing.hsadminng.hs.office.coopassets; package net.hostsharing.hsadminng.hs.office.coopassets;
public enum HsOfficeCoopAssetsTransactionType { public enum HsOfficeCoopAssetsTransactionType {
ADJUSTMENT, DEPOSIT, DISBURSAL, TRANSFER, ADOPTION, CLEARING, LOSS 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
} }

View File

@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -20,7 +21,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DisplayName("CoopShareTransaction") @DisplayName("CoopShareTransaction")
public class HsOfficeCoopSharesTransactionEntity implements Stringifyable { public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, HasUuid {
private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class) private static Stringify<HsOfficeCoopSharesTransactionEntity> stringify = stringify(HsOfficeCoopSharesTransactionEntity.class)
.withProp(e -> e.getMembership().getMemberNumber()) .withProp(e -> e.getMembership().getMemberNumber())

View File

@ -1,5 +1,7 @@
package net.hostsharing.hsadminng.hs.office.coopshares; package net.hostsharing.hsadminng.hs.office.coopshares;
public enum HsOfficeCoopSharesTransactionType { public enum HsOfficeCoopSharesTransactionType {
ADJUSTMENT, SUBSCRIPTION, CANCELLATION; 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
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayName; import net.hostsharing.hsadminng.errors.DisplayName;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.repository.HasUuid; import net.hostsharing.hsadminng.hs.office.migration.HasUuid;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,12 +8,17 @@ create table hs_office_debitor
( (
uuid uuid unique references RbacObject (uuid) initially deferred, uuid uuid unique references RbacObject (uuid) initially deferred,
partnerUuid uuid not null references hs_office_partner(uuid), partnerUuid uuid not null references hs_office_partner(uuid),
debitorNumber numeric(5) not null, billable boolean not null default true,
debitorNumberSuffix numeric(2) not null,
billingContactUuid uuid not null references hs_office_contact(uuid), billingContactUuid uuid not null references hs_office_contact(uuid),
vatId varchar(24), -- TODO.spec: here or in person? vatId varchar(24), -- TODO.spec: here or in person?
vatCountryCode varchar(2), vatCountryCode varchar(2),
vatBusiness boolean not null, -- TODO.spec: more of such? vatBusiness boolean not null, -- TODO.spec: more of such?
refundBankAccountUuid uuid references hs_office_bankaccount(uuid) refundBankAccountUuid uuid references hs_office_bankaccount(uuid),
defaultPrefix char(3) not null
constraint check_member_code check (
defaultPrefix::text ~ '^([a-z]{3}|al0|bh1|c4s|f3k|k8i|l3d|mh1|o13|p2m|s80|t4w)$'
)
-- TODO.impl: SEPA-mandate -- TODO.impl: SEPA-mandate
); );
--// --//

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,10 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.context.ContextBasedTest; import net.hostsharing.hsadminng.context.ContextBasedTest;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; import net.hostsharing.hsadminng.hs.office.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.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeReasonForTermination;
@ -15,23 +19,14 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType;
import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity; import net.hostsharing.hsadminng.hs.office.sepamandate.HsOfficeSepaMandateEntity;
import net.hostsharing.hsadminng.repository.HasUuid;
import net.hostsharing.test.JpaAttempt; import net.hostsharing.test.JpaAttempt;
import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.*;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.Commit; 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.transaction.support.TransactionTemplate;
import org.springframework.util.ReflectionUtils;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
@ -40,12 +35,11 @@ import jakarta.validation.constraints.NotNull;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.math.BigDecimal;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
@ -57,23 +51,26 @@ import static org.assertj.core.api.Assertions.assertThat;
* This 'test' includes the complete legacy 'office' data import. * This 'test' includes the complete legacy 'office' data import.
* *
* There is no code in 'main' because the import is not needed a normal runtime. * There is no code in 'main' because the import is not needed a normal runtime.
* There is some test data in Java resources to verfiy the data conversion. * There is some test data in Java resources to verify the data conversion.
* For a real import a main method will be added later * For a real import a main method will be added later
* which reads CSV files from the file system. * which reads CSV files from the file system.
*/ */
@Disabled
@DataJpaTest @DataJpaTest
@Import({ Context.class, JpaAttempt.class }) @Import({ Context.class, JpaAttempt.class })
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ImportOfficeTables extends ContextBasedTest { public class ImportOfficeTables extends ContextBasedTest {
private static Map<Integer, HsOfficeContactEntity> contacts = new HashMap<>(); private static NavigableMap<Integer, HsOfficeContactEntity> contacts = new TreeMap<>();
private static Map<Integer, HsOfficePersonEntity> persons = new HashMap<>(); private static NavigableMap<Integer, HsOfficePersonEntity> persons = new TreeMap<>();
private static Map<Integer, HsOfficePartnerEntity> partners = new HashMap<>(); private static NavigableMap<Integer, HsOfficePartnerEntity> partners = new TreeMap<>();
private static Map<Integer, HsOfficeDebitorEntity> debitors = new HashMap<>(); private static NavigableMap<Integer, HsOfficeDebitorEntity> debitors = new TreeMap<>();
private static Map<Integer, HsOfficeMembershipEntity> memberships = new HashMap<>(); private static NavigableMap<Integer, HsOfficeMembershipEntity> memberships = new TreeMap<>();
private static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new HashMap<>(); private static NavigableMap<Integer, HsOfficeSepaMandateEntity> sepaMandates = new TreeMap<>();
private static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new HashMap<>(); private static NavigableMap<Integer, HsOfficeBankAccountEntity> bankAccounts = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new TreeMap<>();
private static NavigableMap<Integer, HsOfficeCoopAssetsTransactionEntity> coopAssets = new TreeMap<>();
@PersistenceContext @PersistenceContext
EntityManager em; EntityManager em;
@ -142,9 +139,9 @@ public class ImportOfficeTables extends ContextBasedTest {
"""); """);
assertThat(contacts.toString()).isEqualToIgnoringWhitespace(""" 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'), 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'), 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') 121=contact(label='Paule Schmidt , Test PS', emailAddresses='ps@example.com')
} }
"""); """);
@ -196,6 +193,52 @@ 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 @Test
@Order(10) @Order(10)
@Commit @Commit
@ -219,7 +262,11 @@ public class ImportOfficeTables extends ContextBasedTest {
sepaMandates.forEach((id, mandate) -> em.persist(mandate)); sepaMandates.forEach((id, mandate) -> em.persist(mandate));
updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id"); updateLegacyIds(sepaMandates, "hs_office_sepamandate_legacy_id", "sepa_mandate_id");
// TODO: coopshares+coopassets 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");
em.flush(); em.flush();
} }
@ -263,49 +310,117 @@ public class ImportOfficeTables extends ContextBasedTest {
final var person = HsOfficePersonEntity.builder() final var person = HsOfficePersonEntity.builder()
.personType(HsOfficePersonType.UNKNOWN) // TODO .personType(HsOfficePersonType.UNKNOWN) // TODO
.build(); .build();
persons.put(toInt(rec.get("bp_id")), person); persons.put(rec.getInteger("bp_id"), person);
final var partner = HsOfficePartnerEntity.builder() final var partner = HsOfficePartnerEntity.builder()
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
.contact(HsOfficeContactEntity.builder().build()) .contact(HsOfficeContactEntity.builder().build())
.person(person) .person(person)
.build(); .build();
partners.put(toInt(rec.get("bp_id")), partner); partners.put(rec.getInteger("bp_id"), partner);
final var debitor = HsOfficeDebitorEntity.builder() final var debitor = HsOfficeDebitorEntity.builder()
.partner(partner) .partner(partner)
.debitorNumber(toInt(rec.get("member_id"))) // .debitorNumberSuffix(rec.getByte("member_id"))
// .memberCode(rec.get("member_code")) TODO // .defaultPrefix(rec.getString("member_code"))
.partner(partner) .partner(partner)
.billingContact(partner.getContact()) .billingContact(partner.getContact())
// .memberRoles(toBool(rec.get("member_role")) TODO .billable(rec.isEmpty("free"))
// .authorContract(toBool(rec.get("author_contract")) TODO // .vatExempt(toBool(rec.get("exempt_vat")) (reverse-charge) TODO: add as vat-reverse-charge to debitor
// .nonDisclosureContract(toBool(rec.get("nondisc_contract")) TODO .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove
// .free(toBool(rec.get("free")) TODO .vatId(rec.getString("uid_vat"))
// .vatExempt(toBool(rec.get("exempt_vat")) TODO
.vatBusiness("GROSS".equals(rec.get("indicator_vat")))
.vatId(rec.get("uid_vat"))
.build(); .build();
debitors.put(toInt(rec.get("bp_id")), debitor); debitors.put(rec.getInteger("bp_id"), debitor);
partners.put(toInt(rec.get("bp_id")), partner); partners.put(rec.getInteger("bp_id"), partner);
if (isNotBlank(rec.get("member_since"))) { if (isNotBlank(rec.getString("member_since"))) {
final var membership = HsOfficeMembershipEntity.builder() final var membership = HsOfficeMembershipEntity.builder()
.partner(partner) .partner(partner)
.memberNumber(toInt(rec.get("member_id"))) .memberNumber(rec.getInteger("member_id"))
.validity(toPostgresDateRange(localDate(rec.get("member_since")), localDate(rec.get("member_until")))) .validity(toPostgresDateRange(rec.getLocalDate("member_since"), rec.getLocalDate("member_until")))
.membershipFeeBillable(rec.isEmpty("member_role"))
.reasonForTermination( .reasonForTermination(
isBlank(rec.get("member_until")) isBlank(rec.getString("member_until"))
? HsOfficeReasonForTermination.NONE ? HsOfficeReasonForTermination.NONE
: HsOfficeReasonForTermination.UNKNOWN) // TODO : HsOfficeReasonForTermination.UNKNOWN)
.mainDebitor(debitor) .mainDebitor(debitor)
.build(); .build();
memberships.put(toInt(rec.get("bp_id")), membership); memberships.put(rec.getInteger("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) { private void importSepaMandates(final String[] header, final List<String[]> records) {
final var columns = new Columns(header); final var columns = new Columns(header);
@ -314,25 +429,25 @@ public class ImportOfficeTables extends ContextBasedTest {
.map(this::trimAll) .map(this::trimAll)
.map(row -> new Record(columns, row)) .map(row -> new Record(columns, row))
.forEach(rec -> { .forEach(rec -> {
final var debitor = debitors.get(toInt(rec.get("bp_id"))); final var debitor = debitors.get(rec.getInteger("bp_id"));
final var sepaMandate = HsOfficeSepaMandateEntity.builder() final var sepaMandate = HsOfficeSepaMandateEntity.builder()
.debitor(debitor) .debitor(debitor)
.bankAccount(HsOfficeBankAccountEntity.builder() .bankAccount(HsOfficeBankAccountEntity.builder()
.holder(rec.get("bank_customer")) .holder(rec.getString("bank_customer"))
// .bankName(rec.get("bank_name")) // TODO // .bankName(rec.get("bank_name")) // not supported
.iban(rec.get("bank_iban")) .iban(rec.getString("bank_iban"))
.bic(rec.get("bank_bic")) .bic(rec.getString("bank_bic"))
.build()) .build())
.reference(rec.get("mandat_ref")) .reference(rec.getString("mandat_ref"))
.agreement(LocalDate.parse(rec.get("mandat_signed"))) .agreement(LocalDate.parse(rec.getString("mandat_signed")))
.validity(toPostgresDateRange( .validity(toPostgresDateRange(
toLocalDate(rec.get("mandat_since")), rec.getLocalDate("mandat_since"),
toLocalDate(rec.get("mandat_until")))) rec.getLocalDate("mandat_until")))
.build(); .build();
sepaMandates.put(toInt(rec.get("sepa_mandat_id")), sepaMandate); sepaMandates.put(rec.getInteger("sepa_mandat_id"), sepaMandate);
bankAccounts.put(toInt(rec.get("sepa_mandat_id")), sepaMandate.getBankAccount()); bankAccounts.put(rec.getInteger("sepa_mandat_id"), sepaMandate.getBankAccount());
}); });
} }
@ -344,15 +459,15 @@ public class ImportOfficeTables extends ContextBasedTest {
.map(this::trimAll) .map(this::trimAll)
.map(row -> new Record(columns, row)) .map(row -> new Record(columns, row))
.forEach(rec -> { .forEach(rec -> {
if (isNotBlank(rec.get("roles")) && rec.get("roles").contains("billing")) { if (isNotBlank(rec.getString("roles")) && rec.getString("roles").contains("billing")) {
final var partner = partners.get(toInt(rec.get("bp_id"))); final var partner = partners.get(rec.getInteger("bp_id"));
final var person = partner.getPerson(); final var person = partner.getPerson();
person.setTradeName(rec.get("firma")); person.setTradeName(rec.getString("firma"));
// TODO: title+salutation // TODO: title+salutation: add to person
person.setGivenName(rec.get("first_name")); person.setGivenName(rec.getString("first_name"));
person.setFamilyName(rec.get("last_name")); person.setFamilyName(rec.getString("last_name"));
initContact(partner.getContact(), rec); initContact(partner.getContact(), rec);
@ -364,10 +479,10 @@ public class ImportOfficeTables extends ContextBasedTest {
} }
private void initContact(final HsOfficeContactEntity contact, final Record rec) { private void initContact(final HsOfficeContactEntity contact, final Record rec) {
contacts.put(toInt(rec.get("contact_id")), contact); contacts.put(rec.getInteger("contact_id"), contact);
contact.setLabel(toLabel(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name"), rec.get("firma"))); contact.setLabel(toLabel(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"), rec.getString("firma")));
contact.setEmailAddresses(rec.get("email")); contact.setEmailAddresses(rec.getString("email"));
contact.setPostalAddress(toAddress(rec)); contact.setPostalAddress(toAddress(rec));
contact.setPhoneNumbers(toPhoneNumbers(rec)); contact.setPhoneNumbers(toPhoneNumbers(rec));
} }
@ -382,28 +497,28 @@ public class ImportOfficeTables extends ContextBasedTest {
} }
private String toPhoneNumbers(final Record rec) { private String toPhoneNumbers(final Record rec) {
final var result = new StringBuilder("{\n"); final var result = new StringBuilder("{\n");
if (isNotBlank(rec.get("phone_private"))) if (isNotBlank(rec.getString("phone_private")))
result.append(" \"private\": " + "\"" + rec.get("phone_private") + "\",\n"); result.append(" \"private\": " + "\"" + rec.getString("phone_private") + "\",\n");
if (isNotBlank(rec.get("phone_office"))) if (isNotBlank(rec.getString("phone_office")))
result.append(" \"office\": " + "\"" + rec.get("phone_office") + "\",\n"); result.append(" \"office\": " + "\"" + rec.getString("phone_office") + "\",\n");
if (isNotBlank(rec.get("phone_mobile"))) if (isNotBlank(rec.getString("phone_mobile")))
result.append(" \"mobile\": " + "\"" + rec.get("phone_mobile") + "\",\n"); result.append(" \"mobile\": " + "\"" + rec.getString("phone_mobile") + "\",\n");
if (isNotBlank(rec.get("fax"))) if (isNotBlank(rec.getString("fax")))
result.append(" \"fax\": " + "\"" + rec.get("fax") + "\",\n"); result.append(" \"fax\": " + "\"" + rec.getString("fax") + "\",\n");
return (result + "}").replace("\",\n}", "\"\n}"); return (result + "}").replace("\",\n}", "\"\n}");
} }
private String toAddress(final Record rec) { private String toAddress(final Record rec) {
final var result = new StringBuilder(); final var result = new StringBuilder();
final var name = toName(rec.get("salut"), rec.get("title"), rec.get("first_name"), rec.get("last_name")); final var name = toName(rec.getString("salut"), rec.getString("title"), rec.getString("first_name"), rec.getString("last_name"));
if (isNotBlank(name)) if (isNotBlank(name))
result.append(name + "\n"); result.append(name + "\n");
if (isNotBlank(rec.get("firma"))) if (isNotBlank(rec.getString("firma")))
result.append(rec.get("firma") + "\n"); result.append(rec.getString("firma") + "\n");
if (isNotBlank(rec.get("co"))) if (isNotBlank(rec.getString("co")))
result.append("c/o " + rec.get("co") + "\n"); result.append("c/o " + rec.getString("co") + "\n");
if (isNotBlank(rec.get("street"))) if (isNotBlank(rec.getString("street")))
result.append(rec.get("street") + "\n"); result.append(rec.getString("street") + "\n");
final var zipcodeAndCity = toZipcodeAndCity(rec); final var zipcodeAndCity = toZipcodeAndCity(rec);
if (isNotBlank(zipcodeAndCity)) if (isNotBlank(zipcodeAndCity))
result.append(zipcodeAndCity + "\n"); result.append(zipcodeAndCity + "\n");
@ -412,12 +527,12 @@ public class ImportOfficeTables extends ContextBasedTest {
private String toZipcodeAndCity(final Record rec) { private String toZipcodeAndCity(final Record rec) {
final var result = new StringBuilder(); final var result = new StringBuilder();
if (isNotBlank(rec.get("country"))) if (isNotBlank(rec.getString("country")))
result.append(rec.get("country") + " "); result.append(rec.getString("country") + " ");
if (isNotBlank(rec.get("zipcode"))) if (isNotBlank(rec.getString("zipcode")))
result.append(rec.get("zipcode") + " "); result.append(rec.getString("zipcode") + " ");
if (isNotBlank(rec.get("city"))) if (isNotBlank(rec.getString("city")))
result.append(rec.get("city")); result.append(rec.getString("city"));
return result.toString(); return result.toString();
} }
@ -446,23 +561,6 @@ public class ImportOfficeTables extends ContextBasedTest {
return toLabel(salut, title, firstname, lastname, null); 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) { private Reader resourceReader(@NotNull final String resourcePath) {
return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath)); return new InputStreamReader(getClass().getClassLoader().getResourceAsStream(resourcePath));
@ -512,7 +610,38 @@ class Record {
this.row = row; this.row = row;
} }
String get(final String columnName) { String getString(final String columnName) {
return row[columns.indexOf(columnName)]; return row[columns.indexOf(columnName)];
} }
boolean isEmpty(final String columnName) {
final String value = getString(columnName);
return value == null || value.isBlank();
}
Byte getByte(final String columnName) {
final String value = getString(columnName);
return isNotBlank(value) ? Byte.valueOf(value.trim()) : 0;
}
Integer getInteger(final String columnName) {
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,6 +105,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike("forth contact").get(0);
final var newPartner = toCleanup(HsOfficePartnerEntity.builder() final var newPartner = toCleanup(HsOfficePartnerEntity.builder()
.debitorNumberPrefix(22222)
.person(givenPerson) .person(givenPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())
@ -115,11 +116,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// then // then
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.admin",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.agent", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.agent",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.owner",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant", "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.tenant",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.guest")); "hs_office_partner#22222:ErbenBesslerMelBessler-forthcontact.guest"));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())) assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess")) .map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("forthcontact", "4th")) .map(s -> s.replace("forthcontact", "4th"))
@ -127,31 +128,31 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
.containsExactlyInAnyOrder(Array.fromFormatted( .containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
// owner // owner
"{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }", "{ grant perm * on partner#22222:EBess-4th to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant perm * on partner_details#EBess-4th-details to role partner#EBess-4th.owner by system and assume }", "{ grant perm * on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }", "{ grant role partner#22222:EBess-4th.owner to role global#global.admin by system and assume }",
// admin // admin
"{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }", "{ grant perm edit on partner#22222:EBess-4th to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant perm edit on partner_details#EBess-4th-details to role partner#EBess-4th.admin by system and assume }", "{ grant perm edit on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }", "{ grant role partner#22222:EBess-4th.admin to role partner#22222:EBess-4th.owner by system and assume }",
"{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }", "{ grant role person#EBess.tenant to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }", "{ grant role contact#4th.tenant to role partner#22222:EBess-4th.admin by system and assume }",
// agent // agent
"{ grant perm view on partner_details#EBess-4th-details to role partner#EBess-4th.agent by system and assume }", "{ grant perm view on partner_details#22222:EBess-4th-details to role partner#22222:EBess-4th.agent by system and assume }",
"{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role partner#22222:EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role person#EBess.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }", "{ grant role partner#22222:EBess-4th.agent to role contact#4th.admin by system and assume }",
// tenant // tenant
"{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }", "{ grant role partner#22222:EBess-4th.tenant to role partner#22222:EBess-4th.agent by system and assume }",
"{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role person#EBess.guest to role partner#22222:EBess-4th.tenant by system and assume }",
"{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role contact#4th.guest to role partner#22222:EBess-4th.tenant by system and assume }",
// guest // guest
"{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }", "{ grant perm view on partner#22222:EBess-4th to role partner#22222:EBess-4th.guest by system and assume }",
"{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }", "{ grant role partner#22222:EBess-4th.guest to role partner#22222:EBess-4th.tenant by system and assume }",
null)); null));
} }
@ -217,10 +218,10 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() { public void hostsharingAdmin_withoutAssumedRole_canUpdateArbitraryPartner() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler("fifth contact"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "fifth contact");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_person#ErbenBesslerMelBessler.admin"); "hs_office_partner#22222:ErbenBesslerMelBessler-fifthcontact.admin");
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0); final var givenNewPerson = personRepo.findPersonByOptionalNameLike("Third OHG").get(0);
@ -253,16 +254,16 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void partnerAgent_canNotUpdateRelatedPartner() { public void partnerAgent_canNotUpdateRelatedPartner() {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPartner = givenSomeTemporaryPartnerBessler("ninth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "ninth");
assertThatPartnerIsVisibleForUserWithRole( assertThatPartnerIsVisibleForUserWithRole(
givenPartner, givenPartner,
"hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); "hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
assertThatPartnerActuallyInDatabase(givenPartner); assertThatPartnerActuallyInDatabase(givenPartner);
final var givenNewContact = contactRepo.findContactByOptionalLabelLike("tenth").get(0);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net", "hs_office_partner#ErbenBesslerMelBessler-ninthcontact.agent"); context("superuser-alex@hostsharing.net",
"hs_office_partner#22222:ErbenBesslerMelBessler-ninthcontact.agent");
givenPartner.getDetails().setBirthName("new birthname"); givenPartner.getDetails().setBirthName("new birthname");
return partnerRepo.save(givenPartner); return partnerRepo.save(givenPartner);
}); });
@ -304,7 +305,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() { public void globalAdmin_withoutAssumedRole_canDeleteAnyPartner() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler("tenth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "tenth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -324,7 +325,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() { public void nonGlobalAdmin_canNotDeleteTheirRelatedPartner() {
// given // given
context("superuser-alex@hostsharing.net", null); context("superuser-alex@hostsharing.net", null);
final var givenPartner = givenSomeTemporaryPartnerBessler("eleventh"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "eleventh");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -350,7 +351,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll())); final var initialRoleNames = Array.from(roleNamesOf(rawRoleRepo.findAll()));
final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll()));
final var givenPartner = givenSomeTemporaryPartnerBessler("twelfth"); final var givenPartner = givenSomeTemporaryPartnerBessler(22222, "Erben Bessler", "twelfth");
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
@ -394,12 +395,14 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
}); });
} }
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(final String contact) { private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler(
final Integer debitorNumberPrefix, final String person, final String contact) {
return jpaAttempt.transacted(() -> { return jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var givenPerson = personRepo.findPersonByOptionalNameLike("Erben Bessler").get(0); final var givenPerson = personRepo.findPersonByOptionalNameLike(person).get(0);
final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0); final var givenContact = contactRepo.findContactByOptionalLabelLike(contact).get(0);
final var newPartner = HsOfficePartnerEntity.builder() final var newPartner = HsOfficePartnerEntity.builder()
.debitorNumberPrefix(debitorNumberPrefix)
.person(givenPerson) .person(givenPerson)
.contact(givenContact) .contact(givenContact)
.details(HsOfficePartnerDetailsEntity.builder().build()) .details(HsOfficePartnerDetailsEntity.builder().build())

View File

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

View File

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

View File

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

View File

@ -1,5 +1,9 @@
member_asset_id; bp_id; date; action; amount member_asset_id; bp_id; date; action; amount; comment
33443; 7; 2000-12-06; PAYMENT; 1280 30000; 7; 2000-12-06; PAYMENT; 1280.00; for subscription A
33451; 10; 2000-12-06; PAYMENT; 128 31000; 10; 2000-12-06; PAYMENT; 128.00; for subscription B
33701; 7; 2005-01-10; PAYMENT; 2560 32000; 7; 2005-01-10; PAYMENT; 2560.00; for subscription C
33810; 10; 2016-12-31; PAYBACK; 128 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

1 member_asset_id bp_id date action amount comment
2 33443 30000 7 2000-12-06 PAYMENT 1280 1280.00 for subscription A
3 33451 31000 10 2000-12-06 PAYMENT 128 128.00 for subscription B
4 33701 32000 7 2005-01-10 PAYMENT 2560 2560.00 for subscription C
5 33810 33001 10 7 2016-12-31 2005-01-10 PAYBACK HANDOVER 128 -512.00 for transfer to 10
6 33002 10 2005-01-10 ADOPTION 512.00 for transfer from 7
7 34001 10 2016-12-31 CLEARING -8.00 for cancellation D
8 34002 10 2016-12-31 PAYBACK -100.00 for cancellation D
9 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 bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat
7;10007;mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 7;10007;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007
10;10010;xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; 10;10010;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS;
12;11012;xxx;2021-04-01;;;;;true;true;GROSS; 12;11012;hsh00-xxx;2021-04-01;;;;;true;true;GROSS;

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