Compare commits
8 Commits
feature/in
...
master
Author | SHA1 | Date | |
---|---|---|---|
d7caf3b0f8 | |||
35db9aad43 | |||
c98a5acb38 | |||
8f410198e9 | |||
e97b177a92 | |||
6191bf16e0 | |||
63af33d003 | |||
3b94f117fb |
3
.aliases
3
.aliases
@ -95,3 +95,6 @@ if [ ! -f .environment ]; then
|
||||
cp .tc-environment .environment
|
||||
fi
|
||||
source .environment
|
||||
|
||||
alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office'
|
||||
alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office'
|
||||
|
36
Jenkinsfile
vendored
36
Jenkinsfile
vendored
@ -26,9 +26,35 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Compile & Test') {
|
||||
stage ('Compile') {
|
||||
steps {
|
||||
sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze'
|
||||
sh './gradlew clean processSpring compileJava compileTestJava --no-daemon'
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Tests') {
|
||||
parallel {
|
||||
stage('Unit-/Integration/Acceptance-Tests') {
|
||||
steps {
|
||||
sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets'
|
||||
}
|
||||
}
|
||||
stage('Import-Tests') {
|
||||
steps {
|
||||
sh './gradlew importOfficeData importHostingAssets --no-daemon'
|
||||
}
|
||||
}
|
||||
stage ('Scenario-Tests') {
|
||||
steps {
|
||||
sh './gradlew scenarioTests --no-daemon'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage ('Check') {
|
||||
steps {
|
||||
sh './gradlew check -x pitest -x dependencyCheckAnalyze --no-daemon'
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,6 +71,12 @@ pipeline {
|
||||
sourcePattern: 'src/main/java'
|
||||
)
|
||||
|
||||
// archive scenario-test reports in HTML format
|
||||
sh '''
|
||||
./gradlew convertMarkdownToHtml
|
||||
'''
|
||||
archiveArtifacts artifacts: 'doc/scenarios/*.html', allowEmptyArchive: true
|
||||
|
||||
// cleanup workspace
|
||||
cleanWs()
|
||||
}
|
||||
|
60
build.gradle
60
build.gradle
@ -118,8 +118,8 @@ openapiProcessor {
|
||||
springRoot {
|
||||
processorName 'spring'
|
||||
processor 'io.openapiprocessor:openapi-processor-spring:2022.5'
|
||||
apiPath "$projectDir/src/main/resources/api-definition.yaml"
|
||||
mapping "$projectDir/src/main/resources/api-mappings.yaml"
|
||||
apiPath "$projectDir/src/main/resources/api-definition/api-definition.yaml"
|
||||
mapping "$projectDir/src/main/resources/api-definition/api-mappings.yaml"
|
||||
targetDir "$buildDir/generated/sources/openapi-javax"
|
||||
showWarnings true
|
||||
openApiNullable true
|
||||
@ -255,7 +255,7 @@ test {
|
||||
'net.hostsharing.hsadminng.**.generated.**',
|
||||
]
|
||||
useJUnitPlatform {
|
||||
excludeTags 'import'
|
||||
excludeTags 'importOfficeData', 'importHostingData', 'scenarioTest'
|
||||
}
|
||||
}
|
||||
jacocoTestReport {
|
||||
@ -344,6 +344,17 @@ tasks.register('importHostingAssets', Test) {
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
tasks.register('scenarioTests', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'scenarioTest'
|
||||
}
|
||||
|
||||
group 'verification'
|
||||
description 'run the import jobs as tests'
|
||||
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
// pitest mutation testing
|
||||
pitest {
|
||||
targetClasses = ['net.hostsharing.hsadminng.**']
|
||||
@ -391,3 +402,46 @@ tasks.named("dependencyUpdates").configure {
|
||||
isNonStable(it.candidate.version)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generate HTML from Markdown scenario-test-reports using Pandoc:
|
||||
tasks.register('convertMarkdownToHtml') {
|
||||
description = 'Generates HTML from Markdown scenario-test-reports using Pandoc.'
|
||||
group = 'Conversion'
|
||||
|
||||
// Define the template file and input directory
|
||||
def templateFile = file('doc/scenarios/.template.html')
|
||||
|
||||
// Task configuration and execution
|
||||
doFirst {
|
||||
// Check if pandoc is installed
|
||||
try {
|
||||
exec {
|
||||
commandLine 'pandoc', '--version'
|
||||
}
|
||||
} catch (Exception) {
|
||||
throw new GradleException("Pandoc is not installed or not found in the system path.")
|
||||
}
|
||||
|
||||
// Check if the template file exists
|
||||
if (!templateFile.exists()) {
|
||||
throw new GradleException("Template file 'doc/scenarios/.template.html' not found.")
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
// Gather all Markdown files in the current directory
|
||||
fileTree(dir: '.', include: 'build/doc/scenarios/*.md').each { file ->
|
||||
// Corrected way to create the output file path
|
||||
def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html'))
|
||||
|
||||
// Execute pandoc for each markdown file
|
||||
exec {
|
||||
commandLine 'pandoc', file.absolutePath, '--template', templateFile.absolutePath, '-o', outputFile.absolutePath
|
||||
}
|
||||
|
||||
println "Converted ${file.name} to ${outputFile.name}"
|
||||
}
|
||||
}
|
||||
}
|
||||
convertMarkdownToHtml.dependsOn scenarioTests
|
||||
|
124
doc/business-glossary-de.md
Normal file
124
doc/business-glossary-de.md
Normal file
@ -0,0 +1,124 @@
|
||||
### hsadminNg fachliches Glossar
|
||||
|
||||
<!--
|
||||
Currently, this business glossary is only available in German because in many cases,
|
||||
the German terms are important for comprehensibility for those using this software.
|
||||
-->
|
||||
|
||||
Dieses ist eine Sammlung von Fachbegriffen, die in diesem Projekt benutzt werden.
|
||||
Ebenfalls aufgenommen sind technische Begriffe, die für Benutzer für das Verständnis der Schnittstellen nötig sind.
|
||||
|
||||
Falls etwas fehlt, bitte Bescheid geben.
|
||||
|
||||
|
||||
#### Partner
|
||||
|
||||
In diesem System ist ein _Partner_ grundsätzlich jeglicher Geschäftspartner der _Hostsharing eG_.
|
||||
Dies können grundsätzlich Kunden, siehe [Debitor](#Debitor), wie Lieferanten sein.
|
||||
Derzeit sind aber nur Debitoren implementiert.
|
||||
|
||||
Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`)
|
||||
sowie Zusatzinformationen (z.B. Registergerichtnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden.
|
||||
|
||||
Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor)
|
||||
und zeitlich nacheinander mehrere [Mitgliedschaften](#Mitgliedschaft) geben.
|
||||
|
||||
Partner sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person _Hostsharing eG_ implementiert.
|
||||
|
||||
|
||||
### Debitor
|
||||
|
||||
Ein `Debitor` ist quasi ein Rechnungsempfänger für einen [Partner](#Partner).
|
||||
|
||||
Für einen _Partner_ kann es gleichzeitig mehrere [Debitoren](#Debitor) geben,
|
||||
z.B. für spezielle Projekte des Kunden oder verbundene Organisationen.
|
||||
|
||||
Des Weiteren gibt es für jeden _Partner_ eine fünfstellige Partnernummer mit dem Prefix 'P-' (z.B. `P-123454`)
|
||||
sowie Zusatzinformationen (z.B. Registergerichtsnummer oder Geburtsdatum), die zur genauen Identifikation benötigt werden.
|
||||
|
||||
Debitoren sind grundsätzlich als ist [Relation](#Relation) der Vertragsperson mit der Person des Vertragspartners implementiert.
|
||||
|
||||
|
||||
#### Relation
|
||||
|
||||
Eine _Relation_ ist eine typisierte und mit Kontaktdaten versehene Beziehung einer (_Holder_)-Person zu einer _Anchor_-Person.
|
||||
|
||||
Eine Relation ist eine Art Geschäftsrolle, wir haben hier aber keinen Begriff mit 'Rolle' verwendet,
|
||||
weil 'Role' (engl.) zu leicht mit der [RBAC-Rolle](#RBAC-Role) verwechselt werden könnte.
|
||||
|
||||
Die _Relation_ ist auch ein technisches Konzept und gehört nicht zur Domänensprache.
|
||||
Dieses Konzept ist jedoch für das Verständnis der ([API](#API)) notwendig.
|
||||
|
||||
|
||||
#### Ex-Partner
|
||||
|
||||
Ex-Partner bilden [Personen](#Person) ab, die vormals [Partner](#Partner) waren.
|
||||
Diese bleiben dadurch informationshalber im System verfügbar.
|
||||
|
||||
Implementiert ist der _Ex-Partner_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des Ex-Partner (_Holder_) zum neuen Partner (_Anchor_) dargestellt.
|
||||
Dieses kann zu einer Kettenbildung führen.
|
||||
|
||||
|
||||
#### Representative-Contact (ehemals _contractual_)
|
||||
|
||||
Ein _Representative_ ist eine natürliche Person, die für eine nicht-natürliche Person vertretungsberechtigt ist.
|
||||
|
||||
Implementiert ist der _Representative_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des Repräsentanten (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
||||
|
||||
|
||||
### VIP-Contact
|
||||
|
||||
Ein _VIP-Contact_ ist eine natürliche Person, die für einen Geschäftspartner eine wichtige Funktion übernimmt,
|
||||
nicht aber deren offizieller Repräsentant ist.
|
||||
|
||||
Implementiert ist der _VIP-Contact_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des VIP-Contact (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
||||
|
||||
|
||||
### Operations-Contact
|
||||
|
||||
Ein _Operations-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner technischer Ansprechpartner ist.
|
||||
|
||||
Ein Seiteneffekt ist, dass diese Person im Ticketsystem Znuny direkt dem Geschäftspartner zugeordnet werden kann.
|
||||
|
||||
Im Legacy System waren das die Kontakte mit der Rolle `operation` und `silent`.
|
||||
|
||||
Implementiert ist der _Operations-Contact_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des _Operations-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
||||
|
||||
|
||||
### OperationsAlert-Contact
|
||||
|
||||
Ein _OperationsAlert-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner bei technischen Probleme kontaktiert werden soll.
|
||||
|
||||
Im Legacy System waren das die Kontakte mit der Rolle `operation`.
|
||||
|
||||
Implementiert ist der _OperationsAlert-Contact_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des _OperationsAlert-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
||||
|
||||
|
||||
### Subscriber-Contact
|
||||
|
||||
Ein _Subscriber-_Contact_ ist_ eine natürliche Person, die für einen Geschäftspartner eine bestimmte Mailingliste abonniert.
|
||||
|
||||
Implementiert ist der _Subscriber-Contact_ als eine besondere Form der [Relation](#Relation)
|
||||
der Person des _Subscriber-Contact_ (_Holder_) zur repräsentierten Person (_Anchor_) dargestellt.
|
||||
Zusätzlich wird diese Relation mit dem Kurznamen der abonnierten Mailingliste markiert.
|
||||
|
||||
|
||||
#### Anchor / Relation-Anchor
|
||||
|
||||
siehe [Relation](#Relation)
|
||||
|
||||
|
||||
#### Holder / Relation-Holder
|
||||
|
||||
siehe [Relation](#Relation)
|
||||
|
||||
|
||||
#### API
|
||||
|
||||
Und API (Application-Programming-Interface) verstehen wir eine über HTTPS angesprochene programmatisch bedienbare Schnittstell
|
||||
zur Funktionalität des hsAdmin-NG-Systems.
|
@ -64,7 +64,7 @@ classDiagram
|
||||
}
|
||||
|
||||
class partner-MeierGmbH {
|
||||
+Numeric partnerNumber: 12345
|
||||
+Numeric partnerNumber: P-12345
|
||||
+Relation partnerRel
|
||||
}
|
||||
partner-MeierGmbH *-- rel-MeierGmbH
|
||||
|
124
doc/scenarios/.template.html
Normal file
124
doc/scenarios/.template.html
Normal file
@ -0,0 +1,124 @@
|
||||
<!doctype html>
|
||||
<html $if(lang)$ lang="$lang$" $endif$>
|
||||
<head>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!--[if lt IE 9]>
|
||||
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
|
||||
<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
|
||||
<!-- <link rel="stylesheet" type="text/css" href="template.css" /> -->
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/template.css" />
|
||||
|
||||
<link href="https://vjs.zencdn.net/5.4.4/video-js.css" rel="stylesheet" />
|
||||
|
||||
<script src="https://code.jquery.com/jquery-2.2.1.min.js"></script>
|
||||
<!-- <script type='text/javascript' src='menu/js/jquery.cookie.js'></script> -->
|
||||
<!-- <script type='text/javascript' src='menu/js/jquery.hoverIntent.minified.js'></script> -->
|
||||
<!-- <script type='text/javascript' src='menu/js/jquery.dcjqaccordion.2.7.min.js'></script> -->
|
||||
|
||||
<!-- <link href="menu/css/skins/blue.css" rel="stylesheet" type="text/css" /> -->
|
||||
<!-- <link href="menu/css/skins/graphite.css" rel="stylesheet" type="text/css" /> -->
|
||||
<!-- <link href="menu/css/skins/grey.css" rel="stylesheet" type="text/css" /> -->
|
||||
|
||||
<!-- <script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> -->
|
||||
|
||||
|
||||
<!-- <script src="script.js"></script> -->
|
||||
|
||||
<!-- <script src="jquery.sticky-kit.js "></script> -->
|
||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.cookie.js'></script>
|
||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.hoverIntent.minified.js'></script>
|
||||
<script type='text/javascript' src='https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/js/jquery.dcjqaccordion.2.7.min.js'></script>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/blue.css" rel="stylesheet" type="text/css" />
|
||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/graphite.css" rel="stylesheet" type="text/css" />
|
||||
<link href="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/menu/css/skins/grey.css" rel="stylesheet" type="text/css" />
|
||||
<link href="https://cdn.jsdelivr.net/gh/ryangrose/easy-pandoc-templates@948e28e5/css/elegant_bootstrap.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/script.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/gh/diversen/pandoc-bootstrap-adaptive-template@959c3622/jquery.sticky-kit.js"></script>
|
||||
<meta name="generator" content="pandoc" />
|
||||
$for(author-meta)$
|
||||
<meta name="author" content="$author-meta$" />
|
||||
$endfor$
|
||||
$if(date-meta)$
|
||||
<meta name="date" content="$date-meta$" />
|
||||
$endif$
|
||||
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
$if(quotes)$
|
||||
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style>
|
||||
$endif$
|
||||
$if(highlighting-css)$
|
||||
<style type="text/css">
|
||||
$highlighting-css$
|
||||
</style>
|
||||
$endif$
|
||||
$for(css)$
|
||||
<link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>
|
||||
$endfor$
|
||||
$if(math)$
|
||||
$math$
|
||||
$endif$
|
||||
$for(header-includes)$
|
||||
$header-includes$
|
||||
$endfor$
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
$if(title)$
|
||||
<div class="navbar navbar-static-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<span class="doc-title">$title$</span>
|
||||
<ul class="nav pull-right doc-info">
|
||||
$for(author)$
|
||||
<li><p class="navbar-text">$author$</p></li>
|
||||
$endfor$
|
||||
$if(date)$
|
||||
<li><p class="navbar-text">$date$</p></li>
|
||||
$endif$
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
$endif$
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
$if(toc)$
|
||||
<div id="$idprefix$TOC" class="span3">
|
||||
<div class="well toc">
|
||||
|
||||
$toc$
|
||||
|
||||
</div>
|
||||
</div>
|
||||
$endif$
|
||||
<div class="span$if(toc)$9$else$12$endif$">
|
||||
|
||||
$if(abstract)$
|
||||
<H1>$abstract-title$</H1>
|
||||
$abstract$
|
||||
$endif$
|
||||
|
||||
$for(include-before)$
|
||||
$include-before$
|
||||
$endfor$
|
||||
$body$
|
||||
$for(include-after)$
|
||||
$include-after$
|
||||
$endfor$
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://vjs.zencdn.net/5.4.4/video.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
1
doc/scenarios/README.txt
Normal file
1
doc/scenarios/README.txt
Normal file
@ -0,0 +1 @@
|
||||
find the generated ScenarioReports in build/doc/scenarios
|
@ -90,6 +90,20 @@ Acceptance-tests, are blackbox-tests and do <u>not</u> count into test-code-cove
|
||||
TODO.test: Complete the Acceptance-Tests test concept.
|
||||
|
||||
|
||||
#### Scenario-Tests
|
||||
|
||||
Our Scenario-tests are induced by business use-cases.
|
||||
They test from the REST API all the way down to the database.
|
||||
|
||||
Most scenario-tests are positive tests, they test if business scenarios do work.
|
||||
But few might be negative tests, which test if specific forbidden data gets rejected.
|
||||
|
||||
Our scenario tests also generate test-reports which contain the REST-API calls needed for each scenario.
|
||||
These reports can be used as examples for the API usage from a business perspective.
|
||||
|
||||
There is an extra document regarding scenario-test, see [Scenario-Tests README](../src/test/java/net/hostsharing/hsadminng/hs/office/scenarios/README.md).
|
||||
|
||||
|
||||
#### Performance-Tests
|
||||
|
||||
Performance-critical scenarios have to be identified and a special performance-test has to be implemented.
|
||||
|
@ -1,10 +1,6 @@
|
||||
FROM eclipse-temurin:21-jdk
|
||||
RUN apt-get update && \
|
||||
apt-get install -y bind9-utils && \
|
||||
apt-get install -y bind9-utils pandoc && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# RUN mkdir /opt/app
|
||||
# COPY japp.jar /opt
|
||||
# CMD ["java", "-jar", "/opt/app/japp.jar"]
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.openapitools.jackson.nullable.JsonNullableModule;
|
||||
@ -9,15 +10,20 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class JsonObjectMapperConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public Jackson2ObjectMapperBuilder customObjectMapper() {
|
||||
// HOWTO: add JSON converters and specify other JSON mapping configurations
|
||||
return new Jackson2ObjectMapperBuilder()
|
||||
.modules(new JsonNullableModule(), new JavaTimeModule())
|
||||
.featuresToEnable(JsonParser.Feature.ALLOW_COMMENTS, JsonParser.Feature.ALLOW_COMMENTS)
|
||||
.featuresToEnable(
|
||||
JsonParser.Feature.ALLOW_COMMENTS,
|
||||
DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS
|
||||
)
|
||||
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ public class CustomErrorResponse {
|
||||
this.path = path;
|
||||
this.statusCode = status.value();
|
||||
this.statusPhrase = status.getReasonPhrase();
|
||||
// HOWTO: debug serverside error response - set a breakpoint here
|
||||
this.message = message.startsWith("ERROR: [") ? message : "ERROR: [" + statusCode + "] " + message;
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@ -14,7 +14,7 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
// a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity
|
||||
@Entity
|
||||
|
@ -15,8 +15,8 @@ import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
@ -45,7 +45,7 @@ import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
|
@ -4,14 +4,14 @@ import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
|
@ -15,8 +15,8 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
@ -42,7 +42,7 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
|
@ -5,8 +5,8 @@ import lombok.experimental.FieldNameConstants;
|
||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
@ -16,7 +16,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "bankaccount_rv")
|
||||
|
@ -12,8 +12,8 @@ import lombok.experimental.SuperBuilder;
|
||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
@ -27,7 +27,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
@ -54,8 +54,14 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
||||
@Column(name = "caption")
|
||||
private String caption;
|
||||
|
||||
@Builder.Default
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Type(JsonType.class)
|
||||
@Column(name = "postaladdress")
|
||||
private String postalAddress; // multiline free-format text
|
||||
private Map<String, String> postalAddress = new HashMap<>();
|
||||
|
||||
@Transient
|
||||
private PatchableMapWrapper<String> postalAddressWrapper;
|
||||
|
||||
@Builder.Default
|
||||
@Setter(AccessLevel.NONE)
|
||||
@ -75,6 +81,17 @@ public class HsOfficeContact implements Stringifyable, BaseEntity<HsOfficeContac
|
||||
@Transient
|
||||
private PatchableMapWrapper<String> phoneNumbersWrapper;
|
||||
|
||||
public PatchableMapWrapper<String> getPostalAddress() {
|
||||
return PatchableMapWrapper.of(
|
||||
postalAddressWrapper,
|
||||
(newWrapper) -> {postalAddressWrapper = newWrapper;},
|
||||
postalAddress);
|
||||
}
|
||||
|
||||
public void putPostalAddress(Map<String, String> newPostalAddress) {
|
||||
getPostalAddress().assign(newPostalAddress);
|
||||
}
|
||||
|
||||
public PatchableMapWrapper<String> getEmailAddresses() {
|
||||
return PatchableMapWrapper.of(
|
||||
emailAddressesWrapper,
|
||||
|
@ -18,7 +18,8 @@ class HsOfficeContactEntityPatcher implements EntityPatcher<HsOfficeContactPatch
|
||||
@Override
|
||||
public void apply(final HsOfficeContactPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getCaption()).ifPresent(entity::setCaption);
|
||||
OptionalFromJson.of(resource.getPostalAddress()).ifPresent(entity::setPostalAddress);
|
||||
Optional.ofNullable(resource.getPostalAddress())
|
||||
.ifPresent(r -> entity.getPostalAddress().patch(KeyValueMap.from(resource.getPostalAddress())));
|
||||
Optional.ofNullable(resource.getEmailAddresses())
|
||||
.ifPresent(r -> entity.getEmailAddresses().patch(KeyValueMap.from(resource.getEmailAddresses())));
|
||||
Optional.ofNullable(resource.getPhoneNumbers())
|
||||
|
@ -36,7 +36,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> listCoopAssets(
|
||||
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> getListOfCoopAssets(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid,
|
||||
@ -55,7 +55,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> postNewCoopAssetTransaction(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
||||
@ -77,8 +77,7 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getCoopAssetTransactionByUuid(
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getSingleCoopAssetTransactionByUuid(
|
||||
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
@ -129,9 +128,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
}
|
||||
|
||||
final BiConsumer<HsOfficeCoopAssetsTransactionInsertResource, HsOfficeCoopAssetsTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
if ( resource.getReverseEntryUuid() != null ) {
|
||||
entity.setAdjustedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getReverseEntryUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getReverseEntryUuid()))));
|
||||
if ( resource.getRevertedAssetTxUuid() != null ) {
|
||||
entity.setRevertedAssetTx(coopAssetsTransactionRepo.findByUuid(resource.getRevertedAssetTxUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] reverseEntityUuid %s not found".formatted(resource.getRevertedAssetTxUuid()))));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -10,8 +10,8 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
@ -31,7 +31,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "coopassettx_rv")
|
||||
@ -50,8 +50,8 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getAssetValue)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getReference)
|
||||
.withProp(HsOfficeCoopAssetsTransactionEntity::getComment)
|
||||
.withProp(at -> ofNullable(at.getAdjustedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getAdjustmentAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getRevertedAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getReversalAssetTx()).map(HsOfficeCoopAssetsTransactionEntity::toShortString).orElse(null))
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -77,7 +77,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
||||
* The signed value which directly affects the booking balance.
|
||||
*
|
||||
* <p>This means, that a DEPOSIT is always positive, a DISBURSAL is always negative,
|
||||
* but an ADJUSTMENT can bei either positive or negative.
|
||||
* but an REVERSAL can bei either positive or negative.
|
||||
* See {@link HsOfficeCoopAssetsTransactionType} for</p> more information.
|
||||
*/
|
||||
@Column(name = "assetvalue")
|
||||
@ -96,14 +96,14 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable, BaseE
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
|
||||
* Optionally, the UUID of the corresponding transaction for an reversal transaction.
|
||||
*/
|
||||
@OneToOne
|
||||
@JoinColumn(name = "adjustedassettxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity adjustedAssetTx;
|
||||
@JoinColumn(name = "revertedassettxuuid")
|
||||
private HsOfficeCoopAssetsTransactionEntity revertedAssetTx;
|
||||
|
||||
@OneToOne(mappedBy = "adjustedAssetTx")
|
||||
private HsOfficeCoopAssetsTransactionEntity adjustmentAssetTx;
|
||||
@OneToOne(mappedBy = "revertedAssetTx")
|
||||
private HsOfficeCoopAssetsTransactionEntity reversalAssetTx;
|
||||
|
||||
@Override
|
||||
public HsOfficeCoopAssetsTransactionEntity load() {
|
||||
|
@ -4,7 +4,7 @@ public enum HsOfficeCoopAssetsTransactionType {
|
||||
/**
|
||||
* correction of wrong bookings, value can be positive or negative
|
||||
*/
|
||||
ADJUSTMENT,
|
||||
REVERSAL,
|
||||
|
||||
/**
|
||||
* payment received from member after signing shares, value >0
|
||||
|
@ -38,7 +38,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> listCoopShares(
|
||||
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid,
|
||||
@ -57,7 +57,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> postNewCoopSharesTransaction(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
||||
@ -80,7 +80,7 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getCoopShareTransactionByUuid(
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getSingleCoopShareTransactionByUuid(
|
||||
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
@ -131,9 +131,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
}
|
||||
|
||||
final BiConsumer<HsOfficeCoopSharesTransactionInsertResource, HsOfficeCoopSharesTransactionEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
if ( resource.getAdjustedShareTxUuid() != null ) {
|
||||
entity.setAdjustedShareTx(coopSharesTransactionRepo.findByUuid(resource.getAdjustedShareTxUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] adjustedShareTxUuid %s not found".formatted(resource.getAdjustedShareTxUuid()))));
|
||||
if ( resource.getRevertedShareTxUuid() != null ) {
|
||||
entity.setRevertedShareTx(coopSharesTransactionRepo.findByUuid(resource.getRevertedShareTxUuid())
|
||||
.orElseThrow(() -> new EntityNotFoundException("ERROR: [400] revertedShareTxUuid %s not found".formatted(resource.getRevertedShareTxUuid()))));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
@ -29,7 +29,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "coopsharetx_rv")
|
||||
@ -48,8 +48,8 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getShareCount)
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getReference)
|
||||
.withProp(HsOfficeCoopSharesTransactionEntity::getComment)
|
||||
.withProp(at -> ofNullable(at.getAdjustedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getAdjustmentShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getRevertedShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||
.withProp(at -> ofNullable(at.getReversalShareTx()).map(HsOfficeCoopSharesTransactionEntity::toShortString).orElse(null))
|
||||
.quotedValues(false);
|
||||
|
||||
@Id
|
||||
@ -71,7 +71,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
||||
* The signed value which directly affects the booking balance.
|
||||
*
|
||||
* <p>This means, that a SUBSCRIPTION is always positive, a CANCELLATION is always negative,
|
||||
* but an ADJUSTMENT can bei either positive or negative.
|
||||
* but an REVERSAL can bei either positive or negative.
|
||||
* See {@link HsOfficeCoopSharesTransactionType} for</p> more information.
|
||||
*/
|
||||
@Column(name = "valuedate")
|
||||
@ -93,14 +93,14 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable, BaseE
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* Optionally, the UUID of the corresponding transaction for an adjustment transaction.
|
||||
* Optionally, the UUID of the corresponding transaction for a REVERSAL transaction.
|
||||
*/
|
||||
@OneToOne
|
||||
@JoinColumn(name = "adjustedsharetxuuid")
|
||||
private HsOfficeCoopSharesTransactionEntity adjustedShareTx;
|
||||
@JoinColumn(name = "revertedsharetxuuid")
|
||||
private HsOfficeCoopSharesTransactionEntity revertedShareTx;
|
||||
|
||||
@OneToOne(mappedBy = "adjustedShareTx")
|
||||
private HsOfficeCoopSharesTransactionEntity adjustmentShareTx;
|
||||
@OneToOne(mappedBy = "revertedShareTx")
|
||||
private HsOfficeCoopSharesTransactionEntity reversalShareTx;
|
||||
|
||||
@Override
|
||||
public HsOfficeCoopSharesTransactionEntity load() {
|
||||
|
@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||
|
||||
public enum HsOfficeCoopSharesTransactionType {
|
||||
/**
|
||||
* correction of wrong bookings, with either positive or negative value
|
||||
* reversal of wrong bookings, with either positive or negative value identical to reversed transaction
|
||||
*/
|
||||
ADJUSTMENT,
|
||||
REVERSAL,
|
||||
|
||||
/**
|
||||
* shares signed, e.g. with the declaration of accession, value >0
|
||||
|
@ -22,8 +22,10 @@ import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.validation.ValidationException;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
|
||||
@ -49,24 +51,24 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<HsOfficeDebitorResource>> listDebitors(
|
||||
public ResponseEntity<List<HsOfficeDebitorResource>> getListOfDebitors(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String name,
|
||||
final Integer debitorNumber) {
|
||||
final String debitorNumber) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
|
||||
final var entities = debitorNumber != null
|
||||
? debitorRepo.findDebitorByDebitorNumber(debitorNumber)
|
||||
? debitorRepo.findDebitorByDebitorNumber(cropTag("D-", debitorNumber))
|
||||
: debitorRepo.findDebitorByOptionalNameLike(name);
|
||||
|
||||
final var resources = mapper.mapList(entities, HsOfficeDebitorResource.class);
|
||||
final var resources = mapper.mapList(entities, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<HsOfficeDebitorResource> addDebitor(
|
||||
public ResponseEntity<HsOfficeDebitorResource> postNewDebitor(
|
||||
String currentSubject,
|
||||
String assumedRoles,
|
||||
HsOfficeDebitorInsertResource body) {
|
||||
@ -77,16 +79,13 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
|
||||
Validate.isTrue(body.getDebitorRel() != null || body.getDebitorRelUuid() != null,
|
||||
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found none");
|
||||
Validate.isTrue(body.getDebitorRel() == null ||
|
||||
body.getDebitorRel().getType() == null || DEBITOR.name().equals(body.getDebitorRel().getType()),
|
||||
"ERROR: [400] debitorRel.type must be '"+DEBITOR.name()+"' or null for default");
|
||||
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRel().getMark() == null,
|
||||
"ERROR: [400] debitorRel.mark must be null");
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeDebitorEntity.class);
|
||||
if ( body.getDebitorRel() != null ) {
|
||||
body.getDebitorRel().setType(DEBITOR.name());
|
||||
if (body.getDebitorRel() != null) {
|
||||
final var debitorRel = mapper.map("debitorRel.", body.getDebitorRel(), HsOfficeRelationRealEntity.class);
|
||||
debitorRel.setType(DEBITOR);
|
||||
entityValidator.validateEntityExists("debitorRel.anchorUuid", debitorRel.getAnchor());
|
||||
entityValidator.validateEntityExists("debitorRel.holderUuid", debitorRel.getHolder());
|
||||
entityValidator.validateEntityExists("debitorRel.contactUuid", debitorRel.getContact());
|
||||
@ -95,7 +94,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
final var debitorRelOptional = relrealRepo.findByUuid(body.getDebitorRelUuid());
|
||||
debitorRelOptional.ifPresentOrElse(
|
||||
debitorRel -> {entityToSave.setDebitorRel(relrealRepo.save(debitorRel));},
|
||||
() -> { throw new ValidationException("Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());});
|
||||
() -> {
|
||||
throw new ValidationException(
|
||||
"Unable to find RealRelation by debitorRelUuid: " + body.getDebitorRelUuid());
|
||||
});
|
||||
}
|
||||
|
||||
final var savedEntity = debitorRepo.save(entityToSave);
|
||||
@ -107,13 +109,13 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
.path("/api/hs/office/debitors/{id}")
|
||||
.buildAndExpand(savedEntity.getUuid())
|
||||
.toUri();
|
||||
final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class);
|
||||
final var mapped = mapper.map(savedEntity, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.created(uri).body(mapped);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<HsOfficeDebitorResource> getDebitorByUuid(
|
||||
public ResponseEntity<HsOfficeDebitorResource> getSingleDebitorByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid) {
|
||||
@ -124,7 +126,7 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
if (result.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeDebitorResource.class));
|
||||
return ResponseEntity.ok(mapper.map(result.get(), HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,7 +161,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
|
||||
final var saved = debitorRepo.save(current);
|
||||
Hibernate.initialize(saved);
|
||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class);
|
||||
final var mapped = mapper.map(saved, HsOfficeDebitorResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(mapped);
|
||||
}
|
||||
|
||||
final BiConsumer<HsOfficeDebitorEntity, HsOfficeDebitorResource> ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||
resource.setDebitorNumber(entity.getTaggedDebitorNumber());
|
||||
};
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.GenericGenerator;
|
||||
import org.hibernate.annotations.JoinFormula;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
@ -51,7 +51,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "debitor_rv")
|
||||
@ -142,19 +142,14 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
|
||||
return this;
|
||||
}
|
||||
|
||||
private String getDebitorNumberString() {
|
||||
public String getTaggedDebitorNumber() {
|
||||
return ofNullable(partner)
|
||||
.filter(partner -> debitorNumberSuffix != null)
|
||||
.map(HsOfficePartnerEntity::getPartnerNumber)
|
||||
.map(Object::toString)
|
||||
.map(partnerNumber -> partnerNumber + debitorNumberSuffix)
|
||||
.map(partnerNumber -> DEBITOR_NUMBER_TAG + partnerNumber + debitorNumberSuffix)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public Integer getDebitorNumber() {
|
||||
return ofNullable(getDebitorNumberString()).map(Integer::parseInt).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
@ -162,7 +157,7 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
|
||||
|
||||
@Override
|
||||
public String toShortString() {
|
||||
return DEBITOR_NUMBER_TAG + getDebitorNumberString();
|
||||
return getTaggedDebitorNumber();
|
||||
}
|
||||
|
||||
public static RbacView rbac() {
|
||||
|
@ -16,13 +16,16 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
|
||||
JOIN HsOfficePartnerEntity partner
|
||||
ON partner.partnerRel.holder = debitor.debitorRel.anchor
|
||||
AND partner.partnerRel.type = 'PARTNER' AND debitor.debitorRel.type = 'DEBITOR'
|
||||
WHERE cast(partner.partnerNumber as integer) = :partnerNumber
|
||||
AND cast(debitor.debitorNumberSuffix as integer) = :debitorNumberSuffix
|
||||
""")
|
||||
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int partnerNumber, byte debitorNumberSuffix);
|
||||
WHERE partner.partnerNumber = :partnerNumber
|
||||
AND debitor.debitorNumberSuffix = :debitorNumberSuffix
|
||||
""")
|
||||
List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int partnerNumber, String debitorNumberSuffix);
|
||||
|
||||
default List<HsOfficeDebitorEntity> findDebitorByDebitorNumber(int debitorNumber) {
|
||||
return findDebitorByDebitorNumber( debitorNumber/100, (byte) (debitorNumber%100));
|
||||
final var partnerNumber = debitorNumber / 100;
|
||||
final String suffix = String.format("%02d", debitorNumber % 100);
|
||||
final var result = findDebitorByDebitorNumber(partnerNumber, suffix);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Query("""
|
||||
|
@ -16,8 +16,10 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@RestController
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
@Autowired
|
||||
@ -31,16 +33,18 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<HsOfficeMembershipResource>> listMemberships(
|
||||
public ResponseEntity<List<HsOfficeMembershipResource>> getListOfMemberships(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
UUID partnerUuid,
|
||||
Integer memberNumber) {
|
||||
final UUID partnerUuid,
|
||||
final String memberNumber) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
|
||||
final var entities = ( memberNumber != null)
|
||||
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
|
||||
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
||||
final var entities = (memberNumber != null)
|
||||
? ofNullable(membershipRepo.findMembershipByMemberNumber(
|
||||
cropTag(HsOfficeMembershipEntity.MEMBER_NUMBER_TAG, memberNumber))).stream()
|
||||
.toList()
|
||||
: membershipRepo.findMembershipsByOptionalPartnerUuid(partnerUuid);
|
||||
|
||||
final var resources = mapper.mapList(entities, HsOfficeMembershipResource.class,
|
||||
SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
@ -49,7 +53,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<HsOfficeMembershipResource> addMembership(
|
||||
public ResponseEntity<HsOfficeMembershipResource> postNewMembership(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeMembershipInsertResource body) {
|
||||
@ -72,7 +76,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<HsOfficeMembershipResource> getMembershipByUuid(
|
||||
public ResponseEntity<HsOfficeMembershipResource> getSingleMembershipByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid) {
|
||||
@ -123,7 +127,7 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
}
|
||||
|
||||
final BiConsumer<HsOfficeMembershipEntity, HsOfficeMembershipResource> SEPA_MANDATE_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
|
||||
// TODO.refa: this should be possible via ModelMapper config
|
||||
resource.setMemberNumber(entity.getTaggedMemberNumber());
|
||||
resource.setValidFrom(entity.getValidity().lower());
|
||||
if (entity.getValidity().hasUpperBound()) {
|
||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||
|
@ -13,8 +13,8 @@ import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
@ -53,7 +53,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "membership_rv")
|
||||
@ -130,6 +130,7 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
|
||||
}
|
||||
return validity;
|
||||
}
|
||||
|
||||
public Integer getMemberNumber() {
|
||||
if (partner == null || partner.getPartnerNumber() == null || memberNumberSuffix == null ) {
|
||||
return null;
|
||||
@ -138,6 +139,10 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
|
||||
return getPartner().getPartnerNumber() * 100 + Integer.parseInt(memberNumberSuffix, 10);
|
||||
}
|
||||
|
||||
public String getTaggedMemberNumber() {
|
||||
return MEMBER_NUMBER_TAG + getMemberNumber();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return stringify.apply(this);
|
||||
|
@ -14,14 +14,16 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
||||
|
||||
HsOfficeMembershipEntity save(final HsOfficeMembershipEntity entity);
|
||||
|
||||
List<HsOfficeMembershipEntity> findAll();
|
||||
|
||||
@Query("""
|
||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||
WHERE ( CAST(:partnerUuid as org.hibernate.type.UUIDCharType) IS NULL
|
||||
OR membership.partner.uuid = :partnerUuid )
|
||||
ORDER BY membership.partner.partnerNumber, membership.memberNumberSuffix
|
||||
""")
|
||||
""")
|
||||
List<HsOfficeMembershipEntity> findMembershipsByOptionalPartnerUuid(UUID partnerUuid);
|
||||
|
||||
@Query("""
|
||||
SELECT membership FROM HsOfficeMembershipEntity membership
|
||||
WHERE (:partnerNumber = membership.partner.partnerNumber)
|
||||
@ -31,10 +33,12 @@ public interface HsOfficeMembershipRepository extends Repository<HsOfficeMembers
|
||||
HsOfficeMembershipEntity findMembershipByPartnerNumberAndSuffix(
|
||||
@NotNull Integer partnerNumber,
|
||||
@NotNull String suffix);
|
||||
|
||||
default HsOfficeMembershipEntity findMembershipByMemberNumber(Integer memberNumber) {
|
||||
final var partnerNumber = memberNumber / 100;
|
||||
final var suffix = memberNumber % 100;
|
||||
return findMembershipByPartnerNumberAndSuffix(partnerNumber, String.format("%02d", suffix));
|
||||
final String suffix = String.format("%02d", memberNumber % 100);
|
||||
final var result = findMembershipByPartnerNumberAndSuffix(partnerNumber, suffix);
|
||||
return result;
|
||||
}
|
||||
|
||||
long count();
|
||||
|
@ -27,6 +27,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.EX_PARTNER;
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
|
||||
@ -143,13 +144,14 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
|
||||
private void optionallyCreateExPartnerRelation(final HsOfficePartnerEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
||||
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
||||
// TODO.impl: we also need to use the new partner-person as the anchor
|
||||
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
||||
}
|
||||
}
|
||||
|
||||
private HsOfficePartnerEntity createPartnerEntity(final HsOfficePartnerInsertResource body) {
|
||||
final var entityToSave = new HsOfficePartnerEntity();
|
||||
entityToSave.setPartnerNumber(body.getPartnerNumber());
|
||||
entityToSave.setPartnerNumber(cropTag(HsOfficePartnerEntity.PARTNER_NUMBER_TAG, body.getPartnerNumber()));
|
||||
entityToSave.setPartnerRel(persistPartnerRel(body.getPartnerRel()));
|
||||
entityToSave.setDetails(mapper.map(body.getDetails(), HsOfficePartnerDetailsEntity.class));
|
||||
return entityToSave;
|
||||
|
@ -5,8 +5,8 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import java.io.IOException;
|
||||
@ -17,7 +17,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "partner_details_rv")
|
||||
|
@ -14,8 +14,8 @@ import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.NotFound;
|
||||
import org.hibernate.annotations.NotFoundAction;
|
||||
|
||||
@ -33,7 +33,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "partner_rv")
|
||||
|
@ -6,8 +6,8 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
@ -19,7 +19,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
// TODO.refa: split HsOfficePersonEntity into Real+Rbac-Entity
|
||||
@Entity
|
||||
|
@ -4,6 +4,7 @@ import jakarta.persistence.AttributeConverter;
|
||||
import jakarta.persistence.Converter;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// HOWTO: convert data types for exchange between PostgreSQL and Java/Hibernate/JPA-Entities
|
||||
@Converter(autoApply = true)
|
||||
public class HsOfficePersonTypeConverter implements AttributeConverter<HsOfficePersonType, String> {
|
||||
|
||||
|
@ -6,14 +6,14 @@ import lombok.experimental.SuperBuilder;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@MappedSuperclass
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
|
@ -47,7 +47,7 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
|
||||
OR lower(rel.anchor.givenName) LIKE :personData OR lower(rel.holder.givenName) LIKE :personData )
|
||||
AND ( :contactData IS NULL
|
||||
OR lower(rel.contact.caption) LIKE :contactData
|
||||
OR lower(rel.contact.postalAddress) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.postalAddress AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.emailAddresses AS String)) LIKE :contactData
|
||||
OR lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
|
||||
""")
|
||||
|
@ -8,5 +8,6 @@ public enum HsOfficeRelationType {
|
||||
VIP_CONTACT,
|
||||
DEBITOR,
|
||||
OPERATIONS,
|
||||
OPERATIONS_ALERT,
|
||||
SUBSCRIBER
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
if (entity.getValidity().hasUpperBound()) {
|
||||
resource.setValidTo(entity.getValidity().upper().minusDays(1));
|
||||
}
|
||||
resource.getDebitor().setDebitorNumber(entity.getDebitor().getDebitorNumber());
|
||||
resource.getDebitor().setDebitorNumber(entity.getDebitor().getTaggedDebitorNumber());
|
||||
};
|
||||
|
||||
final BiConsumer<HsOfficeSepaMandateInsertResource, HsOfficeSepaMandateEntity> SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
|
@ -9,8 +9,8 @@ import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
|
||||
import net.hostsharing.hsadminng.persistence.BaseEntity;
|
||||
import net.hostsharing.hsadminng.rbac.generator.RbacView;
|
||||
import net.hostsharing.hsadminng.stringify.Stringify;
|
||||
import net.hostsharing.hsadminng.stringify.Stringifyable;
|
||||
import net.hostsharing.hsadminng.repr.Stringify;
|
||||
import net.hostsharing.hsadminng.repr.Stringifyable;
|
||||
import org.hibernate.annotations.Type;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
@ -30,7 +30,7 @@ import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectRefer
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*;
|
||||
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
|
||||
import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
|
||||
import static net.hostsharing.hsadminng.repr.Stringify.stringify;
|
||||
|
||||
@Entity
|
||||
@Table(schema = "hs_office", name = "sepamandate_rv")
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.stringify;
|
||||
package net.hostsharing.hsadminng.repr;
|
||||
|
||||
import net.hostsharing.hsadminng.errors.DisplayAs;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package net.hostsharing.hsadminng.stringify;
|
||||
package net.hostsharing.hsadminng.repr;
|
||||
|
||||
public interface Stringifyable {
|
||||
|
@ -0,0 +1,15 @@
|
||||
package net.hostsharing.hsadminng.repr;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class TaggedNumber {
|
||||
|
||||
public static Integer cropTag(final String tag, final String taggedNumber) {
|
||||
return taggedNumber.startsWith(tag) ? Integer.valueOf(taggedNumber.substring(tag.length())) : invalidTag(tag, taggedNumber);
|
||||
}
|
||||
|
||||
private static Integer invalidTag(final String tag, final String taggedNumber) {
|
||||
throw new IllegalArgumentException("Expected " + tag + "... but got: " + taggedNumber);
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ public class SystemProcess {
|
||||
|
||||
@Getter
|
||||
private String stdOut;
|
||||
|
||||
@Getter
|
||||
private String stdErr;
|
||||
|
||||
@ -21,7 +22,6 @@ public class SystemProcess {
|
||||
this.processBuilder = new ProcessBuilder(command);
|
||||
}
|
||||
|
||||
|
||||
public String getCommand() {
|
||||
return processBuilder.command().toString();
|
||||
}
|
||||
|
@ -52,11 +52,11 @@ components:
|
||||
HsBookingItemInsert:
|
||||
type: object
|
||||
properties:
|
||||
projectUuid:
|
||||
project.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
parentItemUuid:
|
||||
parentItem.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -77,7 +77,7 @@ components:
|
||||
$ref: '../hs-hosting/hs-hosting-asset-schemas.yaml#/components/schemas/HsHostingAssetAutoInsert'
|
||||
required:
|
||||
- caption
|
||||
- projectUuid
|
||||
- project.uuid
|
||||
- validFrom
|
||||
- resources
|
||||
additionalProperties: false
|
||||
|
@ -25,7 +25,7 @@ components:
|
||||
HsBookingProjectInsert:
|
||||
type: object
|
||||
properties:
|
||||
debitorUuid:
|
||||
debitor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -35,6 +35,6 @@ components:
|
||||
maxLength: 80
|
||||
nullable: false
|
||||
required:
|
||||
- debitorUuid
|
||||
- debitor.uuid
|
||||
- caption
|
||||
additionalProperties: false
|
||||
|
@ -54,7 +54,7 @@ components:
|
||||
caption:
|
||||
type: string
|
||||
nullable: true
|
||||
alarmContactUuid:
|
||||
alarmContact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -64,11 +64,11 @@ components:
|
||||
HsHostingAssetInsert:
|
||||
type: object
|
||||
properties:
|
||||
bookingItemUuid:
|
||||
bookingItem.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
parentAssetUuid:
|
||||
parentAsset.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -84,7 +84,7 @@ components:
|
||||
minLength: 3
|
||||
maxLength: 80
|
||||
nullable: false
|
||||
alarmContactUuid:
|
||||
alarmContact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -99,11 +99,11 @@ components:
|
||||
HsHostingAssetAutoInsert:
|
||||
type: object
|
||||
properties:
|
||||
parentAssetUuid:
|
||||
parentAsset.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
assignedToAssetUuid:
|
||||
assignedToAsset.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
type:
|
||||
@ -118,7 +118,7 @@ components:
|
||||
minLength: 3
|
||||
maxLength: 80
|
||||
nullable: false
|
||||
alarmContactUuid:
|
||||
alarmContact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -147,10 +147,10 @@ components:
|
||||
minLength: 3
|
||||
maxLength: 80
|
||||
nullable: false
|
||||
assignedToAssetUuid:
|
||||
assignedToAsset.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
alarmContactUuid:
|
||||
alarmContact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
|
@ -12,7 +12,7 @@ components:
|
||||
caption:
|
||||
type: string
|
||||
postalAddress:
|
||||
type: string
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
@ -24,7 +24,7 @@ components:
|
||||
caption:
|
||||
type: string
|
||||
postalAddress:
|
||||
type: string
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
@ -39,21 +39,48 @@ components:
|
||||
type: string
|
||||
nullable: true
|
||||
postalAddress:
|
||||
type: string
|
||||
nullable: true
|
||||
$ref: '#/components/schemas/HsOfficeContactPostalAddress'
|
||||
emailAddresses:
|
||||
$ref: '#/components/schemas/HsOfficeContactEmailAddresses'
|
||||
phoneNumbers:
|
||||
$ref: '#/components/schemas/HsOfficeContactPhoneNumbers'
|
||||
|
||||
HsOfficeContactPostalAddress:
|
||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
firm:
|
||||
type: string
|
||||
nullable: true
|
||||
name:
|
||||
type: string
|
||||
nullable: true
|
||||
co:
|
||||
type: string
|
||||
nullable: true
|
||||
street:
|
||||
type: string
|
||||
nullable: true
|
||||
zipcode:
|
||||
type: string
|
||||
nullable: true
|
||||
city:
|
||||
type: string
|
||||
nullable: true
|
||||
country:
|
||||
type: string
|
||||
nullable: true
|
||||
additionalProperties: true
|
||||
|
||||
HsOfficeContactEmailAddresses:
|
||||
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
||||
anyOf:
|
||||
- type: object
|
||||
additionalProperties: true
|
||||
|
||||
HsOfficeContactPhoneNumbers:
|
||||
# forces generating a java.lang.Object containing a Map, instead of class HsOfficeContactEmailAddresses
|
||||
# forces generating a java.lang.Object containing a Map, instead of a class with fixed properties
|
||||
anyOf:
|
||||
- type: object
|
||||
properties:
|
||||
|
@ -6,7 +6,7 @@ components:
|
||||
HsOfficeCoopAssetsTransactionType:
|
||||
type: string
|
||||
enum:
|
||||
- ADJUSTMENT
|
||||
- REVERSAL
|
||||
- DEPOSIT
|
||||
- DISBURSAL
|
||||
- TRANSFER
|
||||
@ -32,15 +32,15 @@ components:
|
||||
type: string
|
||||
comment:
|
||||
type: string
|
||||
adjustedAssetTx:
|
||||
revertedAssetTx:
|
||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||
adjustmentAssetTx:
|
||||
reversalAssetTx:
|
||||
$ref: '#/components/schemas/HsOfficeReferencedCoopAssetsTransaction'
|
||||
|
||||
HsOfficeReferencedCoopAssetsTransaction:
|
||||
description:
|
||||
Similar to `HsOfficeCoopAssetsTransaction` but without the self-referencing properties
|
||||
(`adjustedAssetTx` and `adjustmentAssetTx`), to avoid recursive JSON.
|
||||
(`revertedAssetTx` and `reversalAssetTx`), to avoid recursive JSON.
|
||||
type: object
|
||||
properties:
|
||||
uuid:
|
||||
@ -62,7 +62,7 @@ components:
|
||||
HsOfficeCoopAssetsTransactionInsert:
|
||||
type: object
|
||||
properties:
|
||||
membershipUuid:
|
||||
membership.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -80,11 +80,11 @@ components:
|
||||
maxLength: 48
|
||||
comment:
|
||||
type: string
|
||||
reverseEntryUuid:
|
||||
revertedAssetTx.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- membershipUuid
|
||||
- membership.uuid
|
||||
- transactionType
|
||||
- assetValue
|
||||
- valueDate
|
||||
|
@ -2,7 +2,7 @@ get:
|
||||
tags:
|
||||
- hs-office-coopAssets
|
||||
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
||||
operationId: getCoopAssetTransactionByUuid
|
||||
operationId: getSingleCoopAssetTransactionByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -3,7 +3,7 @@ get:
|
||||
description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles.
|
||||
tags:
|
||||
- hs-office-coopAssets
|
||||
operationId: listCoopAssets
|
||||
operationId: getListOfCoopAssets
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
@ -46,7 +46,7 @@ post:
|
||||
summary: Adds a new cooperative asset transaction.
|
||||
tags:
|
||||
- hs-office-coopAssets
|
||||
operationId: addCoopAssetsTransaction
|
||||
operationId: postNewCoopAssetTransaction
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -6,7 +6,7 @@ components:
|
||||
HsOfficeCoopSharesTransactionType:
|
||||
type: string
|
||||
enum:
|
||||
- ADJUSTMENT
|
||||
- REVERSAL
|
||||
- SUBSCRIPTION
|
||||
- CANCELLATION
|
||||
|
||||
@ -27,15 +27,15 @@ components:
|
||||
type: string
|
||||
comment:
|
||||
type: string
|
||||
adjustedShareTx:
|
||||
revertedShareTx:
|
||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||
adjustmentShareTx:
|
||||
reversalShareTx:
|
||||
$ref: '#/components/schemas/HsOfficeReferencedCoopSharesTransaction'
|
||||
|
||||
HsOfficeReferencedCoopSharesTransaction:
|
||||
description:
|
||||
Similar to `HsOfficeCoopSharesTransaction` but without the self-referencing properties
|
||||
(`adjustedShareTx` and `adjustmentShareTx`), to avoid recursive JSON.
|
||||
(`revertedShareTx` and `reversalShareTx`), to avoid recursive JSON.
|
||||
type: object
|
||||
properties:
|
||||
uuid:
|
||||
@ -56,7 +56,7 @@ components:
|
||||
HsOfficeCoopSharesTransactionInsert:
|
||||
type: object
|
||||
properties:
|
||||
membershipUuid:
|
||||
membership.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -73,11 +73,11 @@ components:
|
||||
maxLength: 48
|
||||
comment:
|
||||
type: string
|
||||
adjustedShareTxUuid:
|
||||
revertedShareTx.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- membershipUuid
|
||||
- membership.uuid
|
||||
- transactionType
|
||||
- shareCount
|
||||
- valueDate
|
||||
|
@ -2,7 +2,7 @@ get:
|
||||
tags:
|
||||
- hs-office-coopShares
|
||||
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
||||
operationId: getCoopShareTransactionByUuid
|
||||
operationId: getSingleCoopShareTransactionByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -3,7 +3,7 @@ get:
|
||||
description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles.
|
||||
tags:
|
||||
- hs-office-coopShares
|
||||
operationId: listCoopShares
|
||||
operationId: getListOfCoopShares
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
@ -46,7 +46,7 @@ post:
|
||||
summary: Adds a new cooperative share transaction.
|
||||
tags:
|
||||
- hs-office-coopShares
|
||||
operationId: addCoopSharesTransaction
|
||||
operationId: postNewCoopSharesTransaction
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -12,15 +12,13 @@ components:
|
||||
debitorRel:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
debitorNumber:
|
||||
type: integer
|
||||
format: int32
|
||||
minimum: 1000000
|
||||
maximum: 9999999
|
||||
type: string
|
||||
minLength: 9
|
||||
maxLength: 9
|
||||
pattern: 'D-[0-9]{7}'
|
||||
debitorNumberSuffix:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 00
|
||||
maximum: 99
|
||||
type: string
|
||||
pattern: '^[0-9][0-9]$'
|
||||
partner:
|
||||
$ref: 'hs-office-partner-schemas.yaml#/components/schemas/HsOfficePartner'
|
||||
billable:
|
||||
@ -43,7 +41,7 @@ components:
|
||||
HsOfficeDebitorPatch:
|
||||
type: object
|
||||
properties:
|
||||
debitorRelUuid:
|
||||
debitorRel.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -63,7 +61,7 @@ components:
|
||||
vatReverseCharge:
|
||||
type: boolean
|
||||
nullable: false
|
||||
refundBankAccountUuid:
|
||||
refundBankAccount.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -76,15 +74,13 @@ components:
|
||||
type: object
|
||||
properties:
|
||||
debitorRel:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationInsert'
|
||||
debitorRelUuid:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationSubInsert'
|
||||
debitorRel.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
debitorNumberSuffix:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 00
|
||||
maximum: 99
|
||||
type: string
|
||||
pattern: '^[0-9][0-9]$'
|
||||
billable:
|
||||
type: boolean
|
||||
vatId:
|
||||
@ -96,7 +92,7 @@ components:
|
||||
type: boolean
|
||||
vatReverseCharge:
|
||||
type: boolean
|
||||
refundBankAccountUuid:
|
||||
refundBankAccount.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
defaultPrefix:
|
||||
|
@ -2,7 +2,7 @@ get:
|
||||
tags:
|
||||
- hs-office-debitors
|
||||
description: 'Fetch a single debitor by its uuid, if visible for the current subject.'
|
||||
operationId: getDebitorByUuid
|
||||
operationId: getSingleDebitorByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -3,7 +3,7 @@ get:
|
||||
description: Returns the list of (optionally filtered) debitors which are visible to the current subject or any of it's assumed roles.
|
||||
tags:
|
||||
- hs-office-debitors
|
||||
operationId: listDebitors
|
||||
operationId: getListOfDebitors
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
@ -17,7 +17,10 @@ get:
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
minLength: 9
|
||||
maxLength: 9
|
||||
pattern: 'D-[0-9]{7}'
|
||||
description: Debitor number of the requested debitor.
|
||||
responses:
|
||||
"200":
|
||||
@ -37,7 +40,7 @@ post:
|
||||
summary: Adds a new debitor.
|
||||
tags:
|
||||
- hs-office-debitors
|
||||
operationId: addDebitor
|
||||
operationId: postNewDebitor
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -26,9 +26,10 @@ components:
|
||||
mainDebitor:
|
||||
$ref: 'hs-office-debitor-schemas.yaml#/components/schemas/HsOfficeDebitor'
|
||||
memberNumber:
|
||||
type: integer
|
||||
minimum: 1000000
|
||||
maximum: 9999999
|
||||
type: string
|
||||
minLength: 9
|
||||
maxLength: 9
|
||||
pattern: 'M-[0-9]{7}'
|
||||
memberNumberSuffix:
|
||||
type: string
|
||||
minLength: 2
|
||||
@ -62,7 +63,7 @@ components:
|
||||
HsOfficeMembershipInsert:
|
||||
type: object
|
||||
properties:
|
||||
partnerUuid:
|
||||
partner.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -86,7 +87,7 @@ components:
|
||||
nullable: false
|
||||
type: boolean
|
||||
required:
|
||||
- partnerUuid
|
||||
- partner.uuid
|
||||
- memberNumberSuffix
|
||||
- validFrom
|
||||
- membershipFeeBillable
|
||||
|
@ -2,7 +2,7 @@ get:
|
||||
tags:
|
||||
- hs-office-memberships
|
||||
description: 'Fetch a single membership by its uuid, if visible for the current subject.'
|
||||
operationId: getMembershipByUuid
|
||||
operationId: getSingleMembershipByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -4,7 +4,7 @@ get:
|
||||
The list can optionally be filtered by either the `partnerUuid` or the `memberNumber` - not both at the same time.
|
||||
tags:
|
||||
- hs-office-memberships
|
||||
operationId: listMemberships
|
||||
operationId: getListOfMemberships
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
@ -19,7 +19,10 @@ get:
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
minLength: 9
|
||||
maxLength: 9
|
||||
pattern: 'M-[0-9]{7}'
|
||||
description: Member number, exclusive to `partnerUuid`.
|
||||
responses:
|
||||
"200":
|
||||
@ -39,7 +42,7 @@ post:
|
||||
summary: Adds a new membership.
|
||||
tags:
|
||||
- hs-office-memberships
|
||||
operationId: addMembership
|
||||
operationId: postNewMembership
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
|
@ -10,10 +10,10 @@ components:
|
||||
type: string
|
||||
format: uuid
|
||||
partnerNumber:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 10000
|
||||
maximum: 99999
|
||||
type: string
|
||||
minLength: 7
|
||||
maxLength: 7
|
||||
pattern: 'P-[0-9]{5}'
|
||||
partnerRel:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelation'
|
||||
details:
|
||||
@ -50,7 +50,7 @@ components:
|
||||
HsOfficePartnerPatch:
|
||||
type: object
|
||||
properties:
|
||||
partnerRelUuid:
|
||||
partnerRel.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
@ -86,10 +86,10 @@ components:
|
||||
type: object
|
||||
properties:
|
||||
partnerNumber:
|
||||
type: integer
|
||||
format: int8
|
||||
minimum: 10000
|
||||
maximum: 99999
|
||||
type: string
|
||||
minLength: 7
|
||||
maxLength: 7
|
||||
pattern: 'P-[0-9]{5}'
|
||||
partnerRel:
|
||||
$ref: '#/components/schemas/HsOfficePartnerRelInsert'
|
||||
details:
|
||||
@ -103,19 +103,19 @@ components:
|
||||
type: object
|
||||
nullable: false
|
||||
properties:
|
||||
anchorUuid:
|
||||
anchor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
holderUuid:
|
||||
holder.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
contactUuid:
|
||||
contact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- anchorUuid
|
||||
- holderUuid
|
||||
- relContactUuid
|
||||
- anchor.uuid
|
||||
- holder.uuid
|
||||
- relContact.uuid
|
||||
|
||||
HsOfficePartnerDetailsInsert:
|
||||
type: object
|
||||
|
@ -13,6 +13,7 @@ components:
|
||||
- REPRESENTATIVE
|
||||
- VIP_CONTACT
|
||||
- OPERATIONS
|
||||
- OPERATIONS_ALERT
|
||||
- SUBSCRIBER
|
||||
|
||||
HsOfficeRelation:
|
||||
@ -36,18 +37,19 @@ components:
|
||||
HsOfficeRelationPatch:
|
||||
type: object
|
||||
properties:
|
||||
contactUuid:
|
||||
contact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
|
||||
# arbitrary relation with explicit type
|
||||
HsOfficeRelationInsert:
|
||||
type: object
|
||||
properties:
|
||||
anchorUuid:
|
||||
anchor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
holderUuid:
|
||||
holder.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
type:
|
||||
@ -56,11 +58,32 @@ components:
|
||||
mark:
|
||||
type: string
|
||||
nullable: true
|
||||
contactUuid:
|
||||
contact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- anchorUuid
|
||||
- holderUuid
|
||||
- anchor.uuid
|
||||
- holder.uuid
|
||||
- type
|
||||
- contactUuid
|
||||
- contact.uuid
|
||||
|
||||
# relation created as a sub-element with implicitly known type
|
||||
HsOfficeRelationSubInsert:
|
||||
type: object
|
||||
properties:
|
||||
anchor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
holder.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
mark:
|
||||
type: string
|
||||
nullable: true
|
||||
contact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- anchor.uuid
|
||||
- holder.uuid
|
||||
- contact.uuid
|
||||
|
@ -48,11 +48,11 @@ components:
|
||||
HsOfficeSepaMandateInsert:
|
||||
type: object
|
||||
properties:
|
||||
debitorUuid:
|
||||
debitor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
bankAccountUuid:
|
||||
bankAccount.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: false
|
||||
@ -72,8 +72,8 @@ components:
|
||||
format: date
|
||||
nullable: true
|
||||
required:
|
||||
- debitorUuid
|
||||
- bankAccountUuid
|
||||
- debitor.uuid
|
||||
- bankAccount.uuid
|
||||
- reference
|
||||
- agreement
|
||||
- validFrom
|
||||
|
@ -7,7 +7,7 @@ get:
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: name
|
||||
- name: iban
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
|
@ -8,21 +8,21 @@ components:
|
||||
properties:
|
||||
grantedByRoleIdName:
|
||||
type: string
|
||||
grantedByRoleUuid:
|
||||
grantedByRole.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
assumed:
|
||||
type: boolean
|
||||
grantedRoleIdName:
|
||||
type: string
|
||||
grantedRoleUuid:
|
||||
grantedRole.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
granteeSubjectName:
|
||||
type: string
|
||||
granteeSubjectUuid:
|
||||
granteeSubject.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required:
|
||||
- grantedRoleUuid
|
||||
- granteeSubjectUuid
|
||||
- grantedRole.uuid
|
||||
- granteeSubject.uuid
|
||||
|
@ -9,7 +9,7 @@ components:
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
objectUuid:
|
||||
object.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
objectTable:
|
||||
|
@ -14,7 +14,7 @@ components:
|
||||
RbacSubjectPermission:
|
||||
type: object
|
||||
properties:
|
||||
objectUuid:
|
||||
object.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
objectTable:
|
||||
@ -23,10 +23,10 @@ components:
|
||||
type: string
|
||||
roleName:
|
||||
type: string
|
||||
roleUuid:
|
||||
role.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
permissionUuid:
|
||||
permission.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
op:
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
@ -102,10 +102,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = OLD.customerUuid INTO oldCustomer;
|
||||
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s', OLD.customerUuid);
|
||||
assert oldCustomer.uuid is not null, format('oldCustomer must not be null for OLD.customerUuid = %s of package', OLD.customerUuid);
|
||||
|
||||
SELECT * FROM rbactest.customer WHERE uuid = NEW.customerUuid INTO newCustomer;
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s', NEW.customerUuid);
|
||||
assert newCustomer.uuid is not null, format('newCustomer must not be null for NEW.customerUuid = %s of package', NEW.customerUuid);
|
||||
|
||||
|
||||
if NEW.customerUuid <> OLD.customerUuid then
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
@ -98,10 +98,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = OLD.packageUuid INTO oldPackage;
|
||||
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s', OLD.packageUuid);
|
||||
assert oldPackage.uuid is not null, format('oldPackage must not be null for OLD.packageUuid = %s of domain', OLD.packageUuid);
|
||||
|
||||
SELECT * FROM rbactest.package WHERE uuid = NEW.packageUuid INTO newPackage;
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s', NEW.packageUuid);
|
||||
assert newPackage.uuid is not null, format('newPackage must not be null for NEW.packageUuid = %s of domain', NEW.packageUuid);
|
||||
|
||||
|
||||
if NEW.packageUuid <> OLD.packageUuid then
|
||||
|
@ -9,7 +9,7 @@ create table if not exists hs_office.contact
|
||||
uuid uuid unique references rbac.object (uuid) initially deferred,
|
||||
version int not null default 0,
|
||||
caption varchar(128) not null,
|
||||
postalAddress text,
|
||||
postalAddress jsonb not null,
|
||||
emailAddresses jsonb not null,
|
||||
phoneNumbers jsonb not null
|
||||
);
|
||||
|
@ -11,7 +11,6 @@
|
||||
create or replace procedure hs_office.contact_create_test_data(contCaption varchar)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
postalAddr varchar;
|
||||
emailAddr varchar;
|
||||
begin
|
||||
emailAddr = 'contact-admin@' || base.cleanIdentifier(contCaption) || '.example.com';
|
||||
@ -19,14 +18,18 @@ begin
|
||||
perform rbac.create_subject(emailAddr);
|
||||
call base.defineContext('creating contact test-data', null, emailAddr);
|
||||
|
||||
postalAddr := E'Vorname Nachname\nStraße Hnr\nPLZ Stadt';
|
||||
|
||||
raise notice 'creating test contact: %', contCaption;
|
||||
insert
|
||||
into hs_office.contact (caption, postaladdress, emailaddresses, phonenumbers)
|
||||
values (
|
||||
contCaption,
|
||||
postalAddr,
|
||||
( '{ ' ||
|
||||
-- '"name": "' || contCaption || '",' ||
|
||||
-- '"street": "Somewhere 1",' ||
|
||||
-- '"zipcode": "12345",' ||
|
||||
-- '"city": "Where-Ever",' ||
|
||||
'"country": "Germany"' ||
|
||||
'}')::jsonb,
|
||||
('{ "main": "' || emailAddr || '" }')::jsonb,
|
||||
('{ "phone_office": "+49 123 1234567" }')::jsonb
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ CREATE TYPE hs_office.RelationType AS ENUM (
|
||||
'DEBITOR',
|
||||
'VIP_CONTACT',
|
||||
'OPERATIONS',
|
||||
'OPERATIONS_ALERT',
|
||||
'SUBSCRIBER');
|
||||
|
||||
CREATE CAST (character varying as hs_office.RelationType) WITH INOUT AS IMPLICIT;
|
||||
@ -28,6 +29,9 @@ create table if not exists hs_office.relation
|
||||
);
|
||||
--//
|
||||
|
||||
-- TODO.impl: unique constraint, to prevent using the same person multiple times as a partner, or better:
|
||||
-- ( anchorUuid, holderUuid, type)
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-office-relation-MAIN-TABLE-JOURNAL endDelimiter:--//
|
||||
|
@ -38,13 +38,13 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.person WHERE uuid = NEW.holderUuid INTO newHolderPerson;
|
||||
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s', NEW.holderUuid);
|
||||
assert newHolderPerson.uuid is not null, format('newHolderPerson must not be null for NEW.holderUuid = %s of relation', NEW.holderUuid);
|
||||
|
||||
SELECT * FROM hs_office.person WHERE uuid = NEW.anchorUuid INTO newAnchorPerson;
|
||||
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s', NEW.anchorUuid);
|
||||
assert newAnchorPerson.uuid is not null, format('newAnchorPerson must not be null for NEW.anchorUuid = %s of relation', NEW.anchorUuid);
|
||||
|
||||
SELECT * FROM hs_office.contact WHERE uuid = NEW.contactUuid INTO newContact;
|
||||
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s', NEW.contactUuid);
|
||||
assert newContact.uuid is not null, format('newContact must not be null for NEW.contactUuid = %s of relation', NEW.contactUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -37,10 +37,10 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'DELETE'), hs_office.relation_OWNER(newPartnerRel));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.relation_TENANT(newPartnerRel));
|
||||
@ -96,16 +96,16 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = OLD.partnerRelUuid INTO oldPartnerRel;
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s', OLD.partnerRelUuid);
|
||||
assert oldPartnerRel.uuid is not null, format('oldPartnerRel must not be null for OLD.partnerRelUuid = %s of partner', OLD.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.partnerRelUuid INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s', NEW.partnerRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerRelUuid = %s of partner', NEW.partnerRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = OLD.detailsUuid INTO oldPartnerDetails;
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s', OLD.detailsUuid);
|
||||
assert oldPartnerDetails.uuid is not null, format('oldPartnerDetails must not be null for OLD.detailsUuid = %s of partner', OLD.detailsUuid);
|
||||
|
||||
SELECT * FROM hs_office.partner_details WHERE uuid = NEW.detailsUuid INTO newPartnerDetails;
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s', NEW.detailsUuid);
|
||||
assert newPartnerDetails.uuid is not null, format('newPartnerDetails must not be null for NEW.detailsUuid = %s of partner', NEW.detailsUuid);
|
||||
|
||||
|
||||
if NEW.partnerRelUuid <> OLD.partnerRelUuid then
|
||||
|
@ -44,10 +44,10 @@ begin
|
||||
WHERE partnerRel.type = 'PARTNER'
|
||||
AND NEW.debitorRelUuid = debitorRel.uuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.relation WHERE uuid = NEW.debitorRelUuid INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s', NEW.debitorRelUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorRelUuid = %s of debitor', NEW.debitorRelUuid);
|
||||
|
||||
SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.refundBankAccountUuid INTO newRefundBankAccount;
|
||||
|
||||
|
@ -37,14 +37,14 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.bankaccount WHERE uuid = NEW.bankAccountUuid INTO newBankAccount;
|
||||
assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s', NEW.bankAccountUuid);
|
||||
assert newBankAccount.uuid is not null, format('newBankAccount must not be null for NEW.bankAccountUuid = %s of sepamandate', NEW.bankAccountUuid);
|
||||
|
||||
SELECT debitorRel.*
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s of sepamandate', NEW.debitorUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -40,7 +40,7 @@ begin
|
||||
JOIN hs_office.relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
|
||||
WHERE partner.uuid = NEW.partnerUuid
|
||||
INTO newPartnerRel;
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s', NEW.partnerUuid);
|
||||
assert newPartnerRel.uuid is not null, format('newPartnerRel must not be null for NEW.partnerUuid = %s of membership', NEW.partnerUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -4,7 +4,7 @@
|
||||
--changeset michael.hoennig:hs-office-coopshares-MAIN-TABLE endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TYPE hs_office.CoopSharesTransactionType AS ENUM ('ADJUSTMENT', 'SUBSCRIPTION', 'CANCELLATION');
|
||||
CREATE TYPE hs_office.CoopSharesTransactionType AS ENUM ('REVERSAL', 'SUBSCRIPTION', 'CANCELLATION');
|
||||
|
||||
CREATE CAST (character varying as hs_office.CoopSharesTransactionType) WITH INOUT AS IMPLICIT;
|
||||
|
||||
@ -17,7 +17,7 @@ create table if not exists hs_office.coopsharetx
|
||||
valueDate date not null,
|
||||
shareCount integer not null,
|
||||
reference varchar(48) not null,
|
||||
adjustedShareTxUuid uuid unique REFERENCES hs_office.coopsharetx(uuid) DEFERRABLE INITIALLY DEFERRED,
|
||||
revertedShareTxUuid uuid unique REFERENCES hs_office.coopsharetx(uuid) DEFERRABLE INITIALLY DEFERRED,
|
||||
comment varchar(512)
|
||||
);
|
||||
--//
|
||||
@ -28,8 +28,8 @@ create table if not exists hs_office.coopsharetx
|
||||
|
||||
alter table hs_office.coopsharetx
|
||||
add constraint reverse_entry_missing
|
||||
check ( transactionType = 'ADJUSTMENT' and adjustedShareTxUuid is not null
|
||||
or transactionType <> 'ADJUSTMENT' and adjustedShareTxUuid is null);
|
||||
check ( transactionType = 'REVERSAL' and revertedShareTxUuid is not null
|
||||
or transactionType <> 'REVERSAL' and revertedShareTxUuid is null);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership;
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopshares', NEW.membershipUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership));
|
||||
|
@ -7,7 +7,7 @@
|
||||
--changeset michael.hoennig:hs-office-coopshares-MIGRATION-mapping endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TABLE hs_office.coopsharestransaction_legacy_id
|
||||
CREATE TABLE hs_office.coopsharetx_legacy_id
|
||||
(
|
||||
uuid uuid NOT NULL REFERENCES hs_office.coopsharetx(uuid),
|
||||
member_share_id integer NOT NULL
|
||||
@ -19,10 +19,10 @@ CREATE TABLE hs_office.coopsharestransaction_legacy_id
|
||||
--changeset michael.hoennig:hs-office-coopshares-MIGRATION-sequence endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE SEQUENCE IF NOT EXISTS hs_office.coopsharestransaction_legacy_id_seq
|
||||
CREATE SEQUENCE IF NOT EXISTS hs_office.coopsharetx_legacy_id_seq
|
||||
AS integer
|
||||
START 1000000000
|
||||
OWNED BY hs_office.coopsharestransaction_legacy_id.member_share_id;
|
||||
OWNED BY hs_office.coopsharetx_legacy_id.member_share_id;
|
||||
--//
|
||||
|
||||
|
||||
@ -30,9 +30,9 @@ CREATE SEQUENCE IF NOT EXISTS hs_office.coopsharestransaction_legacy_id_seq
|
||||
--changeset michael.hoennig:hs-office-coopshares-MIGRATION-default endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
ALTER TABLE hs_office.coopsharestransaction_legacy_id
|
||||
ALTER TABLE hs_office.coopsharetx_legacy_id
|
||||
ALTER COLUMN member_share_id
|
||||
SET DEFAULT nextVal('hs_office.coopsharestransaction_legacy_id_seq');
|
||||
SET DEFAULT nextVal('hs_office.coopsharetx_legacy_id_seq');
|
||||
|
||||
--/
|
||||
|
||||
@ -41,8 +41,8 @@ ALTER TABLE hs_office.coopsharestransaction_legacy_id
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CALL base.defineContext('schema-migration');
|
||||
INSERT INTO hs_office.coopsharestransaction_legacy_id(uuid, member_share_id)
|
||||
SELECT uuid, nextVal('hs_office.coopsharestransaction_legacy_id_seq') FROM hs_office.coopsharetx;
|
||||
INSERT INTO hs_office.coopsharetx_legacy_id(uuid, member_share_id)
|
||||
SELECT uuid, nextVal('hs_office.coopsharetx_legacy_id_seq') FROM hs_office.coopsharetx;
|
||||
--/
|
||||
|
||||
|
||||
@ -58,8 +58,8 @@ begin
|
||||
raise exception 'invalid usage of trigger';
|
||||
end if;
|
||||
|
||||
INSERT INTO hs_office.coopsharestransaction_legacy_id VALUES
|
||||
(NEW.uuid, nextVal('hs_office.coopsharestransaction_legacy_id_seq'));
|
||||
INSERT INTO hs_office.coopsharetx_legacy_id VALUES
|
||||
(NEW.uuid, nextVal('hs_office.coopsharetx_legacy_id_seq'));
|
||||
|
||||
return NEW;
|
||||
end; $$;
|
||||
@ -83,7 +83,7 @@ begin
|
||||
raise exception 'invalid usage of trigger';
|
||||
end if;
|
||||
|
||||
DELETE FROM hs_office.coopsharestransaction_legacy_id
|
||||
DELETE FROM hs_office.coopsharetx_legacy_id
|
||||
WHERE uuid = OLD.uuid;
|
||||
|
||||
return OLD;
|
||||
|
@ -27,12 +27,12 @@ begin
|
||||
raise notice 'creating test coopSharesTransaction: %', givenPartnerNumber::text || givenMemberNumberSuffix;
|
||||
subscriptionEntryUuid := uuid_generate_v4();
|
||||
insert
|
||||
into hs_office.coopsharetx(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment, adjustedShareTxUuid)
|
||||
into hs_office.coopsharetx(uuid, membershipuuid, transactiontype, valuedate, sharecount, reference, comment, revertedShareTxUuid)
|
||||
values
|
||||
(uuid_generate_v4(), membership.uuid, 'SUBSCRIPTION', '2010-03-15', 4, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-1', 'initial subscription', null),
|
||||
(uuid_generate_v4(), membership.uuid, 'CANCELLATION', '2021-09-01', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-2', 'cancelling some', null),
|
||||
(subscriptionEntryUuid, membership.uuid, 'SUBSCRIPTION', '2022-10-20', 2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-3', 'some subscription', null),
|
||||
(uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-4', 'some adjustment', subscriptionEntryUuid);
|
||||
(uuid_generate_v4(), membership.uuid, 'REVERSAL', '2022-10-21', -2, 'ref '||givenPartnerNumber::text || givenMemberNumberSuffix||'-4', 'some reversal', subscriptionEntryUuid);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
--changeset michael.hoennig:hs-office-coopassets-MAIN-TABLE endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TYPE hs_office.CoopAssetsTransactionType AS ENUM ('ADJUSTMENT',
|
||||
CREATE TYPE hs_office.CoopAssetsTransactionType AS ENUM ('REVERSAL',
|
||||
'DEPOSIT',
|
||||
'DISBURSAL',
|
||||
'TRANSFER',
|
||||
@ -22,9 +22,9 @@ create table if not exists hs_office.coopassettx
|
||||
membershipUuid uuid not null references hs_office.membership(uuid),
|
||||
transactionType hs_office.CoopAssetsTransactionType not null,
|
||||
valueDate date not null,
|
||||
assetValue money not null,
|
||||
assetValue numeric(12,2) not null, -- https://wiki.postgresql.org/wiki/Don't_Do_This#Don.27t_use_money
|
||||
reference varchar(48) not null,
|
||||
adjustedAssetTxUuid uuid unique REFERENCES hs_office.coopassettx(uuid) DEFERRABLE INITIALLY DEFERRED,
|
||||
revertedAssetTxUuid uuid unique REFERENCES hs_office.coopassettx(uuid) DEFERRABLE INITIALLY DEFERRED,
|
||||
comment varchar(512)
|
||||
);
|
||||
--//
|
||||
@ -36,20 +36,20 @@ create table if not exists hs_office.coopassettx
|
||||
|
||||
alter table hs_office.coopassettx
|
||||
add constraint reverse_entry_missing
|
||||
check ( transactionType = 'ADJUSTMENT' and adjustedAssetTxUuid is not null
|
||||
or transactionType <> 'ADJUSTMENT' and adjustedAssetTxUuid is null);
|
||||
check ( transactionType = 'REVERSAL' and revertedAssetTxUuid is not null
|
||||
or transactionType <> 'REVERSAL' and revertedAssetTxUuid is null);
|
||||
--//
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-office-coopassets-ASSET-VALUE-CONSTRAINT endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue money)
|
||||
create or replace function hs_office.coopassetstx_check_positive_total(forMembershipUuid UUID, newAssetValue numeric(12, 5))
|
||||
returns boolean
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentAssetValue money;
|
||||
totalAssetValue money;
|
||||
currentAssetValue numeric(12,2);
|
||||
totalAssetValue numeric(12,2);
|
||||
begin
|
||||
select sum(cat.assetValue)
|
||||
from hs_office.coopassettx cat
|
||||
|
@ -36,7 +36,7 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.membership WHERE uuid = NEW.membershipUuid INTO newMembership;
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s', NEW.membershipUuid);
|
||||
assert newMembership.uuid is not null, format('newMembership must not be null for NEW.membershipUuid = %s of coopasset', NEW.membershipUuid);
|
||||
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'SELECT'), hs_office.membership_AGENT(newMembership));
|
||||
call rbac.grantPermissionToRole(rbac.createPermission(NEW.uuid, 'UPDATE'), hs_office.membership_ADMIN(newMembership));
|
||||
|
@ -27,12 +27,12 @@ begin
|
||||
raise notice 'creating test coopAssetsTransaction: %', givenPartnerNumber || givenMemberNumberSuffix;
|
||||
lossEntryUuid := uuid_generate_v4();
|
||||
insert
|
||||
into hs_office.coopassettx(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment, adjustedAssetTxUuid)
|
||||
into hs_office.coopassettx(uuid, membershipuuid, transactiontype, valuedate, assetvalue, reference, comment, revertedAssetTxUuid)
|
||||
values
|
||||
(uuid_generate_v4(), membership.uuid, 'DEPOSIT', '2010-03-15', 320.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-1', 'initial deposit', null),
|
||||
(uuid_generate_v4(), membership.uuid, 'DISBURSAL', '2021-09-01', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-2', 'partial disbursal', null),
|
||||
(lossEntryUuid, membership.uuid, 'DEPOSIT', '2022-10-20', 128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some loss', null),
|
||||
(uuid_generate_v4(), membership.uuid, 'ADJUSTMENT', '2022-10-21', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some adjustment', lossEntryUuid);
|
||||
(uuid_generate_v4(), membership.uuid, 'REVERSAL', '2022-10-21', -128.00, 'ref '||givenPartnerNumber || givenMemberNumberSuffix||'-3', 'some reversal', lossEntryUuid);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
@ -37,14 +37,14 @@ begin
|
||||
call rbac.enterTriggerForObjectUuid(NEW.uuid);
|
||||
|
||||
SELECT * FROM hs_office.debitor WHERE uuid = NEW.debitorUuid INTO newDebitor;
|
||||
assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitor.uuid is not null, format('newDebitor must not be null for NEW.debitorUuid = %s of project', NEW.debitorUuid);
|
||||
|
||||
SELECT debitorRel.*
|
||||
FROM hs_office.relation debitorRel
|
||||
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
|
||||
WHERE debitor.uuid = NEW.debitorUuid
|
||||
INTO newDebitorRel;
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s', NEW.debitorUuid);
|
||||
assert newDebitorRel.uuid is not null, format('newDebitorRel must not be null for NEW.debitorUuid = %s or project', NEW.debitorUuid);
|
||||
|
||||
|
||||
perform rbac.defineRoleWithGrants(
|
||||
|
@ -0,0 +1,8 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset timotheus.pokorra:hs-integration-SCHEMA endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE SCHEMA hs_integration;
|
||||
--//
|
@ -0,0 +1,18 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
-- ============================================================================
|
||||
--changeset timotheus.pokorra:hs-global-integration-kimai endDelimiter:--//
|
||||
-- TODO.impl: also select column debitorNumber and do not filter anymore for '00'
|
||||
CREATE OR REPLACE VIEW hs_integration.time_customer AS
|
||||
SELECT p.partnernumber, debitor.defaultprefix
|
||||
FROM hs_office.partner p
|
||||
JOIN hs_office.relation AS pRel
|
||||
ON pRel.type = 'PARTNER'
|
||||
AND pRel.uuid = p.partnerRelUuid
|
||||
JOIN hs_office.relation AS dRel
|
||||
ON dRel.type = 'DEBITOR'
|
||||
AND dRel.anchorUuid = pRel.holderUuid
|
||||
JOIN hs_office.debitor AS debitor
|
||||
ON debitor.debitorreluuid = dRel.uuid
|
||||
AND debitor.debitornumbersuffix = '00';
|
||||
--//
|
@ -0,0 +1,102 @@
|
||||
|
||||
--liquibase formatted sql
|
||||
|
||||
-- ============================================================================
|
||||
--changeset timotheus.pokorra:hs-global-integration-znuny endDelimiter:--//
|
||||
-- TODO.impl: also select column debitorNumber and do not filter anymore for '00'
|
||||
CREATE OR REPLACE VIEW hs_integration.contact AS
|
||||
SELECT DISTINCT ON (uuid)
|
||||
partner.partnernumber as partnernumber,
|
||||
debitor.defaultprefix as defaultprefix,
|
||||
c.uuid as uuid,
|
||||
(CASE WHEN per.salutation <> '' THEN per.salutation ELSE NULL END) as salutation,
|
||||
(CASE WHEN per.givenname <> '' THEN per.givenname ELSE NULL END) as givenname,
|
||||
(CASE WHEN per.familyname <> '' THEN per.familyname ELSE NULL END) as familyname,
|
||||
(CASE WHEN per.title <> '' THEN per.title ELSE NULL END) as title,
|
||||
(CASE WHEN per.tradename <> '' THEN per.tradename ELSE NULL END) as tradename,
|
||||
(CASE WHEN c.postaladdress->>'co' <> '' THEN c.postaladdress->>'co' ELSE NULL END) as co,
|
||||
c.postaladdress->>'street' as street,
|
||||
c.postaladdress->>'zipcode' as zipcode,
|
||||
c.postaladdress->>'city' as city,
|
||||
c.postaladdress->>'country' as country,
|
||||
c.phonenumbers->>'phone_private' as phone_private,
|
||||
c.phonenumbers->>'phone_office' as phone_office,
|
||||
c.phonenumbers->>'phone_mobile' as phone_mobile,
|
||||
c.phonenumbers->>'fax' as fax,
|
||||
c.emailaddresses->>'main' as email
|
||||
FROM hs_office.partner AS partner
|
||||
JOIN hs_office.partner_legacy_id AS partner_lid ON partner_lid.uuid = partner.uuid
|
||||
JOIN hs_office.relation AS pRel
|
||||
ON pRel.type = 'PARTNER'
|
||||
AND pRel.uuid = partner.partnerRelUuid
|
||||
JOIN hs_office.relation AS dRel
|
||||
ON dRel.type = 'DEBITOR'
|
||||
AND dRel.anchorUuid = pRel.holderUuid
|
||||
JOIN hs_office.debitor AS debitor
|
||||
ON debitor.debitorreluuid = dRel.uuid
|
||||
AND debitor.debitornumbersuffix = '00'
|
||||
JOIN hs_office.contact AS c ON c.uuid = pRel.contactuuid
|
||||
JOIN hs_office.person AS per ON per.uuid = pRel.holderuuid
|
||||
UNION
|
||||
SELECT DISTINCT ON (uuid)
|
||||
partner.partnernumber as partnernumber,
|
||||
debitor.defaultprefix as defaultprefix,
|
||||
c.uuid as uuid,
|
||||
(CASE WHEN per.salutation <> '' THEN per.salutation ELSE NULL END) as salutation,
|
||||
(CASE WHEN per.givenname <> '' THEN per.givenname ELSE NULL END) as givenname,
|
||||
(CASE WHEN per.familyname <> '' THEN per.familyname ELSE NULL END) as familyname,
|
||||
(CASE WHEN per.title <> '' THEN per.title ELSE NULL END) as title,
|
||||
(CASE WHEN per.tradename <> '' THEN per.tradename ELSE NULL END) as tradename,
|
||||
(CASE WHEN c.postaladdress->>'co' <> '' THEN c.postaladdress->>'co' ELSE NULL END) as co,
|
||||
c.postaladdress->>'street' as street,
|
||||
c.postaladdress->>'zipcode' as zipcode,
|
||||
c.postaladdress->>'city' as city,
|
||||
c.postaladdress->>'country' as country,
|
||||
c.phonenumbers->>'phone_private' as phone_private,
|
||||
c.phonenumbers->>'phone_office' as phone_office,
|
||||
c.phonenumbers->>'phone_mobile' as phone_mobile,
|
||||
c.phonenumbers->>'fax' as fax,
|
||||
c.emailaddresses->>'main' as email
|
||||
FROM hs_office.partner AS partner
|
||||
JOIN hs_office.relation AS pRel
|
||||
ON pRel.type = 'PARTNER'
|
||||
AND pRel.uuid = partner.partnerRelUuid
|
||||
JOIN hs_office.relation AS dRel
|
||||
ON dRel.type = 'DEBITOR'
|
||||
AND dRel.anchorUuid = pRel.holderUuid
|
||||
JOIN hs_office.debitor AS debitor
|
||||
ON debitor.debitorreluuid = dRel.uuid
|
||||
AND debitor.debitornumbersuffix = '00'
|
||||
JOIN hs_office.relation AS rs1 ON rs1.uuid = partner.partnerreluuid AND rs1.type = 'PARTNER'
|
||||
JOIN hs_office.relation AS relation ON relation.anchoruuid = rs1.holderuuid
|
||||
JOIN hs_office.contact AS c ON c.uuid = relation.contactuuid
|
||||
JOIN hs_office.person AS per ON per.uuid = relation.holderuuid;
|
||||
|
||||
CREATE OR REPLACE VIEW hs_integration.ticket_customer_user AS
|
||||
SELECT c.uuid,
|
||||
max(c.partnernumber)::text as number,
|
||||
max(c.defaultprefix) as code,
|
||||
max(c.email) as login,
|
||||
max(c.salutation) as salut,
|
||||
max(c.givenname) as firstname,
|
||||
max(c.familyname) as lastname,
|
||||
max(c.title) as title,
|
||||
max(c.tradename) as firma,
|
||||
max(c.co) as co,
|
||||
max(c.street) as street,
|
||||
max(c.zipcode) as zipcode,
|
||||
max(c.city) as city,
|
||||
max(c.country) as country,
|
||||
max(concat_ws(', '::text, c.phone_office, c.phone_private)) AS phone,
|
||||
max(c.phone_private) as phone_private,
|
||||
max(c.phone_office) as phone_office,
|
||||
max(c.phone_mobile) as mobile,
|
||||
max(c.fax) as fax,
|
||||
max(c.email) as email,
|
||||
string_agg(CASE WHEN relation.mark IS NULL THEN relation.type::text ELSE CONCAT(relation.type::text, ':', relation.mark::text) END, '/'::text) AS comment,
|
||||
1 AS valid
|
||||
FROM hs_integration.contact AS c
|
||||
JOIN hs_office.relation AS relation ON c.uuid = relation.contactuuid
|
||||
WHERE (c.defaultprefix != 'hsh' OR (c.partnernumber = 10000 AND c.email = 'hostmaster@hostsharing.net'))
|
||||
GROUP BY c.uuid;
|
||||
--//
|
@ -171,3 +171,9 @@ databaseChangeLog:
|
||||
file: db/changelog/7-hs-hosting/701-hosting-asset/7018-hs-hosting-asset-test-data.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9000-statistics.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9100-hs-integration-schema.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9110-integration-kimai.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9120-integration-znuny.sql
|
@ -46,6 +46,7 @@ public class ArchitectureTest {
|
||||
"..lambda",
|
||||
"..generated..",
|
||||
"..persistence..",
|
||||
"..reflection",
|
||||
"..system..",
|
||||
"..validation..",
|
||||
"..hs.office.bankaccount",
|
||||
@ -54,6 +55,7 @@ public class ArchitectureTest {
|
||||
"..hs.office.coopshares",
|
||||
"..hs.office.debitor",
|
||||
"..hs.office.membership",
|
||||
"..hs.office.scenarios..",
|
||||
"..hs.migration",
|
||||
"..hs.office.partner",
|
||||
"..hs.office.person",
|
||||
@ -75,7 +77,7 @@ public class ArchitectureTest {
|
||||
"..rbac.grant",
|
||||
"..rbac.role",
|
||||
"..rbac.object",
|
||||
"..stringify"
|
||||
"..repr"
|
||||
// ATTENTION: Don't simply add packages here, also add arch rules for the new package!
|
||||
);
|
||||
|
||||
@ -96,7 +98,7 @@ public class ArchitectureTest {
|
||||
public static final ArchRule testClassesAreProperlyNamed = classes()
|
||||
.that().haveSimpleNameEndingWith("Test")
|
||||
.and().doNotHaveModifier(ABSTRACT)
|
||||
.should().haveNameMatching(".*(UnitTest|RestTest|IntegrationTest|AcceptanceTest|ArchitectureTest)$");
|
||||
.should().haveNameMatching(".*(UnitTest|RestTest|IntegrationTest|AcceptanceTest|ScenarioTest|ArchitectureTest)$");
|
||||
|
||||
@ArchTest
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -150,7 +150,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validTo": "{validTo}",
|
||||
@ -200,8 +200,8 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"parentItemUuid": "{managedServerUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"parentItem.uuid": "{managedServerUuid}",
|
||||
"type": "MANAGED_WEBSPACE",
|
||||
"caption": "some managed webspace",
|
||||
"resources": {
|
||||
@ -270,7 +270,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"type": "DOMAIN_SETUP",
|
||||
"caption": "Domain-Setup for example.org",
|
||||
"resources": {
|
||||
@ -285,7 +285,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_MBOX_SETUP"
|
||||
@ -360,7 +360,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"type": "DOMAIN_SETUP",
|
||||
"caption": "some new domain-setup booking",
|
||||
"resources": {
|
||||
@ -375,7 +375,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_MBOX_SETUP"
|
||||
|
@ -105,7 +105,7 @@ class HsBookingItemControllerRestTest {
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validTo": "{validTo}",
|
||||
@ -155,7 +155,7 @@ class HsBookingItemControllerRestTest {
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content("""
|
||||
{
|
||||
"projectUuid": "{projectUuid}",
|
||||
"project.uuid": "{projectUuid}",
|
||||
"type": "MANAGED_SERVER",
|
||||
"caption": "some new booking",
|
||||
"validFrom": "{validFrom}",
|
||||
|
@ -92,7 +92,7 @@ class HsBookingProjectControllerAcceptanceTest extends ContextBasedTestWithClean
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"debitorUuid": "%s",
|
||||
"debitor.uuid": "%s",
|
||||
"caption": "some new project"
|
||||
}
|
||||
""".formatted(givenDebitor.getUuid()))
|
||||
|
@ -165,10 +165,10 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"bookingItemUuid": "%s",
|
||||
"bookingItem.uuid": "%s",
|
||||
"type": "MANAGED_WEBSPACE",
|
||||
"identifier": "fir10",
|
||||
"parentAssetUuid": "%s",
|
||||
"parentAsset.uuid": "%s",
|
||||
"caption": "some separate ManagedWebspace HA",
|
||||
"config": {}
|
||||
}
|
||||
@ -227,7 +227,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"parentAssetUuid": "%s",
|
||||
"parentAsset.uuid": "%s",
|
||||
"type": "UNIX_USER",
|
||||
"identifier": "fir01-temp",
|
||||
"caption": "some new UnixUser in client's ManagedWebspace",
|
||||
@ -280,7 +280,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"bookingItemUuid": "%s",
|
||||
"bookingItem.uuid": "%s",
|
||||
"type": "DOMAIN_SETUP",
|
||||
"identifier": "example.com",
|
||||
"caption": "some unrelated domain-setup",
|
||||
@ -326,7 +326,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"bookingItemUuid": "%s",
|
||||
"bookingItem.uuid": "%s",
|
||||
"type": "MANAGED_SERVER",
|
||||
"identifier": "vm1400",
|
||||
"caption": "some new ManagedServer",
|
||||
@ -381,7 +381,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"parentAssetUuid": "%s",
|
||||
"parentAsset.uuid": "%s",
|
||||
"type": "UNIX_USER",
|
||||
"identifier": "fir01-extra",
|
||||
"caption": "some extra UnixUser",
|
||||
@ -508,7 +508,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"alarmContactUuid": "%s",
|
||||
"alarmContact.uuid": "%s",
|
||||
"config": {
|
||||
"monit_max_ssd_usage": 85,
|
||||
"monit_max_hdd_usage": null,
|
||||
|
@ -110,7 +110,6 @@ public class HsHostingAssetControllerRestTest {
|
||||
"caption": "some fake cloud-server",
|
||||
"alarmContact": {
|
||||
"caption": "some contact",
|
||||
"postalAddress": "address of some contact",
|
||||
"emailAddresses": {
|
||||
"main": "some-contact@example.com"
|
||||
}
|
||||
@ -141,7 +140,6 @@ public class HsHostingAssetControllerRestTest {
|
||||
"caption": "some fake managed-server",
|
||||
"alarmContact": {
|
||||
"caption": "some contact",
|
||||
"postalAddress": "address of some contact",
|
||||
"emailAddresses": {
|
||||
"main": "some-contact@example.com"
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ class DomainSetupHostingAssetFactoryUnitTest {
|
||||
"subHostingAssets": [
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserHostingAssetUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_DNS_SETUP"
|
||||
@ -124,7 +124,7 @@ class DomainSetupHostingAssetFactoryUnitTest {
|
||||
"subHostingAssets": [
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserHostingAssetUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_DNS_SETUP"
|
||||
@ -164,7 +164,7 @@ class DomainSetupHostingAssetFactoryUnitTest {
|
||||
"subHostingAssets": [
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserHostingAssetUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_DNS_SETUP"
|
||||
@ -206,7 +206,7 @@ class DomainSetupHostingAssetFactoryUnitTest {
|
||||
"subHostingAssets": [
|
||||
{
|
||||
"type": "DOMAIN_HTTP_SETUP",
|
||||
"assignedToAssetUuid": "{unixUserHostingAssetUuid}"
|
||||
"assignedToAsset.uuid": "{unixUserHostingAssetUuid}"
|
||||
},
|
||||
{
|
||||
"type": "DOMAIN_DNS_SETUP"
|
||||
|
@ -54,7 +54,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
"subscriber:customers-announce"
|
||||
};
|
||||
private static final String[] KNOWN_ROLES = ArrayUtils.addAll(
|
||||
new String[] { "partner", "vip-contact", "ex-partner", "billing", "contractual", "operation" },
|
||||
new String[] { "partner", "vip-contact", "ex-partner", "billing", "contractual", "operation", "silent" },
|
||||
SUBSCRIBER_ROLES);
|
||||
|
||||
// at least as the number of lines in business_partners.csv from test-data, but less than real data partner count
|
||||
@ -65,18 +65,21 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
static int relationId = INITIAL_RELATION_ID;
|
||||
|
||||
private static final List<Integer> IGNORE_BUSINESS_PARTNERS = Arrays.asList(
|
||||
512167, // 11139, partner without contractual contact
|
||||
512170, // 11142, partner without contractual contact
|
||||
511725, // 10764, partner without contractual contact
|
||||
// 512171, // 11143, partner without partner contact -- exception
|
||||
-1
|
||||
);
|
||||
|
||||
private static final List<Integer> IGNORE_CONTACTS = Arrays.asList(
|
||||
90547, // Kontakt hat keine Rolle
|
||||
-1
|
||||
);
|
||||
|
||||
private static final Map<Integer, HsOfficePersonType> PERSON_TYPES_BY_CONTACT = Map.of(
|
||||
90072, HsOfficePersonType.NATURAL_PERSON,
|
||||
90641, HsOfficePersonType.LEGAL_PERSON,
|
||||
90368, HsOfficePersonType.LEGAL_PERSON,
|
||||
90564, HsOfficePersonType.NATURAL_PERSON,
|
||||
-1, HsOfficePersonType.LEGAL_PERSON
|
||||
);
|
||||
|
||||
static Map<Integer, HsOfficeContactRealEntity> contacts = new WriteOnceMap<>();
|
||||
static Map<Integer, HsOfficePersonEntity> persons = new WriteOnceMap<>();
|
||||
static Map<Integer, HsOfficePartnerEntity> partners = new WriteOnceMap<>();
|
||||
@ -192,56 +195,56 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
|
||||
assertThat(toJsonFormattedString(partners)).isEqualToIgnoringWhitespace("""
|
||||
{
|
||||
100=partner(P-10003: ?? Michael Mellis, Herr Michael Mellis , Michael Mellis),
|
||||
120=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH),
|
||||
122=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS),
|
||||
132=partner(P-10152: ?? Ragnar IT-Beratung, Herr Ragnar Richter , Ragnar IT-Beratung),
|
||||
190=partner(P-19090: NP Camus, Cecilia, Frau Cecilia Camus ),
|
||||
100=partner(P-10003: ?? Michael Mellis, Herr Michael Mellis, Michael Mellis),
|
||||
120=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract, JM GmbH),
|
||||
122=partner(P-11022: ?? Test PS, Petra Schmidt, Test PS),
|
||||
132=partner(P-10152: ?? Ragnar IT-Beratung, Herr Ragnar Richter, Ragnar IT-Beratung),
|
||||
190=partner(P-19090: NP Camus, Cecilia, Frau Cecilia Camus),
|
||||
199=partner(P-19999: null null, null),
|
||||
213=partner(P-10000: LP Hostsharing e.G., Firma Hostmaster Hostsharing , Hostsharing e.G.),
|
||||
541=partner(P-11018: ?? Wasserwerk Südholstein, Frau Christiane Milberg , Wasserwerk Südholstein),
|
||||
542=partner(P-11019: ?? Das Perfekte Haus, Herr Richard Wiese , Das Perfekte Haus)
|
||||
213=partner(P-10000: LP Hostsharing e.G., Firma Hostmaster Hostsharing, Hostsharing e.G.),
|
||||
541=partner(P-11018: ?? Wasserwerk Südholstein, Frau Christiane Milberg, Wasserwerk Südholstein),
|
||||
542=partner(P-11019: ?? Das Perfekte Haus, Herr Richard Wiese, Das Perfekte Haus)
|
||||
}
|
||||
""");
|
||||
assertThat(toJsonFormattedString(contacts)).isEqualToIgnoringWhitespace("""
|
||||
{
|
||||
100=contact(caption='Herr Michael Mellis , Michael Mellis', emailAddresses='{ "main": "michael@Mellis.example.org"}'),
|
||||
100=contact(caption='Herr Michael Mellis, Michael Mellis', emailAddresses='{ "main": "michael@Mellis.example.org"}'),
|
||||
1200=contact(caption='JM e.K.', emailAddresses='{ "main": "jm-ex-partner@example.org"}'),
|
||||
1201=contact(caption='Frau Dr. Jenny Meyer-Billing , JM GmbH', emailAddresses='{ "main": "jm-billing@example.org"}'),
|
||||
1202=contact(caption='Herr Andrew Meyer-Operation , JM GmbH', emailAddresses='{ "main": "am-operation@example.org"}'),
|
||||
1203=contact(caption='Herr Philip Meyer-Contract , JM GmbH', emailAddresses='{ "main": "pm-partner@example.org"}'),
|
||||
1204=contact(caption='Frau Tammy Meyer-VIP , JM GmbH', emailAddresses='{ "main": "tm-vip@example.org"}'),
|
||||
1301=contact(caption='Petra Schmidt , Test PS', emailAddresses='{ "main": "ps@example.com"}'),
|
||||
132=contact(caption='Herr Ragnar Richter , Ragnar IT-Beratung', emailAddresses='{ "main": "hostsharing@ragnar-richter.de"}'),
|
||||
1401=contact(caption='Frau Frauke Fanninga ', emailAddresses='{ "main": "ff@example.org"}'),
|
||||
1501=contact(caption='Frau Cecilia Camus ', emailAddresses='{ "main": "cc@example.org"}'),
|
||||
212=contact(caption='Firma Hostmaster Hostsharing , Hostsharing e.G.', emailAddresses='{ "main": "hostmaster@hostsharing.net"}'),
|
||||
90436=contact(caption='Frau Christiane Milberg , Wasserwerk Südholstein', emailAddresses='{ "main": "rechnung@ww-sholst.example.org"}'),
|
||||
90437=contact(caption='Herr Richard Wiese , Das Perfekte Haus', emailAddresses='{ "main": "admin@das-perfekte-haus.example.org"}'),
|
||||
90438=contact(caption='Herr Karim Metzger , Wasswerwerk Südholstein', emailAddresses='{ "main": "karim.metzger@ww-sholst.example.org"}'),
|
||||
90590=contact(caption='Herr Inhaber R. Wiese , Das Perfekte Haus', emailAddresses='{ "main": "515217@kkemail.example.org"}'),
|
||||
90629=contact(caption='Ragnar Richter ', emailAddresses='{ "main": "mail@ragnar-richter..example.org"}'),
|
||||
90677=contact(caption='Eike Henning ', emailAddresses='{ "main": "hostsharing@eike-henning..example.org"}'),
|
||||
90698=contact(caption='Jan Henning ', emailAddresses='{ "main": "mail@jan-henning.example.org"}')
|
||||
1201=contact(caption='Frau Dr. Jenny Meyer-Billing, JM GmbH', emailAddresses='{ "main": "jm-billing@example.org"}'),
|
||||
1202=contact(caption='Herr Andrew Meyer-Operation, JM GmbH', emailAddresses='{ "main": "am-operation@example.org"}'),
|
||||
1203=contact(caption='Herr Philip Meyer-Contract, JM GmbH', emailAddresses='{ "main": "pm-partner@example.org"}'),
|
||||
1204=contact(caption='Frau Tammy Meyer-VIP, JM GmbH', emailAddresses='{ "main": "tm-vip@example.org"}'),
|
||||
1301=contact(caption='Petra Schmidt, Test PS', emailAddresses='{ "main": "ps@example.com"}'),
|
||||
132=contact(caption='Herr Ragnar Richter, Ragnar IT-Beratung', emailAddresses='{ "main": "hostsharing@ragnar-richter.de"}'),
|
||||
1401=contact(caption='Frau Frauke Fanninga', emailAddresses='{ "main": "ff@example.org"}'),
|
||||
1501=contact(caption='Frau Cecilia Camus', emailAddresses='{ "main": "cc@example.org"}'),
|
||||
212=contact(caption='Firma Hostmaster Hostsharing, Hostsharing e.G.', emailAddresses='{ "main": "hostmaster@hostsharing.net"}'),
|
||||
90436=contact(caption='Frau Christiane Milberg, Wasserwerk Südholstein', emailAddresses='{ "main": "rechnung@ww-sholst.example.org"}'),
|
||||
90437=contact(caption='Herr Richard Wiese, Das Perfekte Haus', emailAddresses='{ "main": "admin@das-perfekte-haus.example.org"}'),
|
||||
90438=contact(caption='Herr Karim Metzger, Wasswerwerk Südholstein', emailAddresses='{ "main": "karim.metzger@ww-sholst.example.org"}'),
|
||||
90590=contact(caption='Herr Inhaber R. Wiese, Das Perfekte Haus', emailAddresses='{ "main": "515217@kkemail.example.org"}'),
|
||||
90629=contact(caption='Ragnar Richter', emailAddresses='{ "main": "mail@ragnar-richter..example.org"}'),
|
||||
90677=contact(caption='Eike Henning', emailAddresses='{ "main": "hostsharing@eike-henning..example.org"}'),
|
||||
90698=contact(caption='Jan Henning', emailAddresses='{ "main": "mail@jan-henning.example.org"}')
|
||||
}
|
||||
""");
|
||||
assertThat(toJsonFormattedString(persons)).isEqualToIgnoringWhitespace("""
|
||||
{
|
||||
100=person(personType='??', tradeName='Michael Mellis', familyName='Mellis', givenName='Michael'),
|
||||
100=person(personType='??', tradeName='Michael Mellis', salutation='Herr', familyName='Mellis', givenName='Michael'),
|
||||
1200=person(personType='LP', tradeName='JM e.K.'),
|
||||
1201=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Billing', givenName='Jenny'),
|
||||
1202=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Operation', givenName='Andrew'),
|
||||
1203=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'),
|
||||
1204=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-VIP', givenName='Tammy'),
|
||||
1201=person(personType='LP', tradeName='JM GmbH', salutation='Frau', title='Dr.', familyName='Meyer-Billing', givenName='Jenny'),
|
||||
1202=person(personType='LP', tradeName='JM GmbH', salutation='Herr', familyName='Meyer-Operation', givenName='Andrew'),
|
||||
1203=person(personType='LP', tradeName='JM GmbH', salutation='Herr', familyName='Meyer-Contract', givenName='Philip'),
|
||||
1204=person(personType='LP', tradeName='JM GmbH', salutation='Frau', familyName='Meyer-VIP', givenName='Tammy'),
|
||||
1301=person(personType='??', tradeName='Test PS', familyName='Schmidt', givenName='Petra'),
|
||||
132=person(personType='??', tradeName='Ragnar IT-Beratung', familyName='Richter', givenName='Ragnar'),
|
||||
1401=person(personType='NP', familyName='Fanninga', givenName='Frauke'),
|
||||
1501=person(personType='NP', familyName='Camus', givenName='Cecilia'),
|
||||
212=person(personType='LP', tradeName='Hostsharing e.G.', familyName='Hostsharing', givenName='Hostmaster'),
|
||||
90436=person(personType='??', tradeName='Wasserwerk Südholstein', familyName='Milberg', givenName='Christiane'),
|
||||
90437=person(personType='??', tradeName='Das Perfekte Haus', familyName='Wiese', givenName='Richard'),
|
||||
90438=person(personType='??', tradeName='Wasswerwerk Südholstein', familyName='Metzger', givenName='Karim'),
|
||||
90590=person(personType='??', tradeName='Das Perfekte Haus', familyName='Wiese', givenName='Inhaber R.'),
|
||||
132=person(personType='??', tradeName='Ragnar IT-Beratung', salutation='Herr', familyName='Richter', givenName='Ragnar'),
|
||||
1401=person(personType='NP', salutation='Frau', familyName='Fanninga', givenName='Frauke'),
|
||||
1501=person(personType='NP', salutation='Frau', familyName='Camus', givenName='Cecilia'),
|
||||
212=person(personType='LP', tradeName='Hostsharing e.G.', salutation='Firma', familyName='Hostsharing', givenName='Hostmaster'),
|
||||
90436=person(personType='??', tradeName='Wasserwerk Südholstein', salutation='Frau', familyName='Milberg', givenName='Christiane'),
|
||||
90437=person(personType='??', tradeName='Das Perfekte Haus', salutation='Herr', familyName='Wiese', givenName='Richard'),
|
||||
90438=person(personType='??', tradeName='Wasswerwerk Südholstein', salutation='Herr', familyName='Metzger', givenName='Karim'),
|
||||
90590=person(personType='??', tradeName='Das Perfekte Haus', salutation='Herr', familyName='Wiese', givenName='Inhaber R.'),
|
||||
90629=person(personType='NP', familyName='Richter', givenName='Ragnar'),
|
||||
90677=person(personType='NP', familyName='Henning', givenName='Eike'),
|
||||
90698=person(personType='NP', familyName='Henning', givenName='Jan')
|
||||
@ -272,71 +275,81 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
""");
|
||||
assertThat(toJsonFormattedString(relations)).isEqualToIgnoringWhitespace("""
|
||||
{
|
||||
2000000=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000001=rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000002=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'),
|
||||
2000003=rel(anchor='?? Ragnar IT-Beratung', type='DEBITOR', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'),
|
||||
2000004=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'),
|
||||
2000005=rel(anchor='LP Hostsharing e.G.', type='DEBITOR', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'),
|
||||
2000006=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000007=rel(anchor='?? Wasserwerk Südholstein', type='DEBITOR', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000008=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000009=rel(anchor='?? Das Perfekte Haus', type='DEBITOR', holder='?? Das Perfekte Haus', contact='Herr Inhaber R. Wiese , Das Perfekte Haus'),
|
||||
2000010=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
|
||||
2000011=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'),
|
||||
2000012=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
|
||||
2000013=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
|
||||
2000014=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
|
||||
2000015=rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
|
||||
2000000=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000001=rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000002=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000003=rel(anchor='?? Ragnar IT-Beratung', type='DEBITOR', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000004=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing, Hostsharing e.G.'),
|
||||
2000005=rel(anchor='LP Hostsharing e.G.', type='DEBITOR', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing, Hostsharing e.G.'),
|
||||
2000006=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000007=rel(anchor='?? Wasserwerk Südholstein', type='DEBITOR', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000008=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000009=rel(anchor='?? Das Perfekte Haus', type='DEBITOR', holder='?? Das Perfekte Haus', contact='Herr Inhaber R. Wiese, Das Perfekte Haus'),
|
||||
2000010=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract, JM GmbH'),
|
||||
2000011=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing, JM GmbH'),
|
||||
2000012=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt, Test PS'),
|
||||
2000013=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt, Test PS'),
|
||||
2000014=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus'),
|
||||
2000015=rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus'),
|
||||
2000016=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='null null, null'),
|
||||
2000017=rel(anchor='null null, null', type='DEBITOR'),
|
||||
2000018=rel(anchor='LP Hostsharing e.G.', type='OPERATIONS', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'),
|
||||
2000019=rel(anchor='LP Hostsharing e.G.', type='REPRESENTATIVE', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'),
|
||||
2000020=rel(anchor='?? Michael Mellis', type='OPERATIONS', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000021=rel(anchor='?? Michael Mellis', type='REPRESENTATIVE', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000022=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-discussion', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000023=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-announce', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000024=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='generalversammlung', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000025=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='members-announce', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000026=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='members-discussion', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'),
|
||||
2000027=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'),
|
||||
2000028=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-discussion', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'),
|
||||
2000029=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-announce', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'),
|
||||
2000030=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
|
||||
2000031=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
|
||||
2000032=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
|
||||
2000033=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'),
|
||||
2000034=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
|
||||
2000035=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
|
||||
2000036=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'),
|
||||
2000037=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'),
|
||||
2000038=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
|
||||
2000039=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'),
|
||||
2000040=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '),
|
||||
2000041=rel(anchor='NP Camus, Cecilia', type='OPERATIONS', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
|
||||
2000042=rel(anchor='NP Camus, Cecilia', type='REPRESENTATIVE', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '),
|
||||
2000043=rel(anchor='?? Wasserwerk Südholstein', type='REPRESENTATIVE', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000044=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='generalversammlung', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000045=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='members-announce', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000046=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='members-discussion', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'),
|
||||
2000047=rel(anchor='?? Das Perfekte Haus', type='OPERATIONS', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000048=rel(anchor='?? Das Perfekte Haus', type='REPRESENTATIVE', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000049=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='operations-discussion', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000050=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='operations-announce', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000051=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='generalversammlung', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000052=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='members-announce', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000053=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='members-discussion', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'),
|
||||
2000054=rel(anchor='?? Wasserwerk Südholstein', type='OPERATIONS', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger , Wasswerwerk Südholstein'),
|
||||
2000055=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='operations-discussion', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger , Wasswerwerk Südholstein'),
|
||||
2000056=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='operations-announce', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger , Wasswerwerk Südholstein'),
|
||||
2000057=rel(anchor='?? Ragnar IT-Beratung', type='REPRESENTATIVE', holder='NP Richter, Ragnar', contact='Ragnar Richter '),
|
||||
2000058=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='generalversammlung', holder='NP Richter, Ragnar', contact='Ragnar Richter '),
|
||||
2000059=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='members-announce', holder='NP Richter, Ragnar', contact='Ragnar Richter '),
|
||||
2000060=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='members-discussion', holder='NP Richter, Ragnar', contact='Ragnar Richter '),
|
||||
2000061=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='NP Henning, Eike', contact='Eike Henning '),
|
||||
2000062=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-discussion', holder='NP Henning, Eike', contact='Eike Henning '),
|
||||
2000063=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-announce', holder='NP Henning, Eike', contact='Eike Henning '),
|
||||
2000064=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='NP Henning, Jan', contact='Jan Henning ')
|
||||
2000018=rel(anchor='LP Hostsharing e.G.', type='OPERATIONS_ALERT', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing, Hostsharing e.G.'),
|
||||
2000019=rel(anchor='LP Hostsharing e.G.', type='OPERATIONS', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing, Hostsharing e.G.'),
|
||||
2000020=rel(anchor='LP Hostsharing e.G.', type='REPRESENTATIVE', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing, Hostsharing e.G.'),
|
||||
2000021=rel(anchor='?? Michael Mellis', type='OPERATIONS_ALERT', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000022=rel(anchor='?? Michael Mellis', type='OPERATIONS', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000023=rel(anchor='?? Michael Mellis', type='REPRESENTATIVE', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000024=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-discussion', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000025=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-announce', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000026=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='generalversammlung', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000027=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='members-announce', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000028=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='members-discussion', holder='?? Michael Mellis', contact='Herr Michael Mellis, Michael Mellis'),
|
||||
2000029=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS_ALERT', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000030=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000031=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-discussion', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000032=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-announce', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter, Ragnar IT-Beratung'),
|
||||
2000033=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'),
|
||||
2000034=rel(anchor='LP JM GmbH', type='OPERATIONS_ALERT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation, JM GmbH'),
|
||||
2000035=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation, JM GmbH'),
|
||||
2000036=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation, JM GmbH'),
|
||||
2000037=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation, JM GmbH'),
|
||||
2000038=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract, JM GmbH'),
|
||||
2000039=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract, JM GmbH'),
|
||||
2000040=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract, JM GmbH'),
|
||||
2000041=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP, JM GmbH'),
|
||||
2000042=rel(anchor='?? Test PS', type='OPERATIONS_ALERT', holder='?? Test PS', contact='Petra Schmidt, Test PS'),
|
||||
2000043=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt, Test PS'),
|
||||
2000044=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt, Test PS'),
|
||||
2000045=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga'),
|
||||
2000046=rel(anchor='NP Camus, Cecilia', type='OPERATIONS_ALERT', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus'),
|
||||
2000047=rel(anchor='NP Camus, Cecilia', type='OPERATIONS', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus'),
|
||||
2000048=rel(anchor='NP Camus, Cecilia', type='REPRESENTATIVE', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus'),
|
||||
2000049=rel(anchor='?? Wasserwerk Südholstein', type='REPRESENTATIVE', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000050=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='generalversammlung', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000051=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='members-announce', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000052=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='members-discussion', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg, Wasserwerk Südholstein'),
|
||||
2000053=rel(anchor='?? Das Perfekte Haus', type='OPERATIONS_ALERT', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000054=rel(anchor='?? Das Perfekte Haus', type='OPERATIONS', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000055=rel(anchor='?? Das Perfekte Haus', type='REPRESENTATIVE', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000056=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='operations-discussion', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000057=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='operations-announce', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000058=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='generalversammlung', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000059=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='members-announce', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000060=rel(anchor='?? Das Perfekte Haus', type='SUBSCRIBER', mark='members-discussion', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese, Das Perfekte Haus'),
|
||||
2000061=rel(anchor='?? Wasserwerk Südholstein', type='OPERATIONS_ALERT', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger, Wasswerwerk Südholstein'),
|
||||
2000062=rel(anchor='?? Wasserwerk Südholstein', type='OPERATIONS', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger, Wasswerwerk Südholstein'),
|
||||
2000063=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='operations-discussion', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger, Wasswerwerk Südholstein'),
|
||||
2000064=rel(anchor='?? Wasserwerk Südholstein', type='SUBSCRIBER', mark='operations-announce', holder='?? Wasswerwerk Südholstein', contact='Herr Karim Metzger, Wasswerwerk Südholstein'),
|
||||
2000065=rel(anchor='?? Ragnar IT-Beratung', type='REPRESENTATIVE', holder='NP Richter, Ragnar', contact='Ragnar Richter'),
|
||||
2000066=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='generalversammlung', holder='NP Richter, Ragnar', contact='Ragnar Richter'),
|
||||
2000067=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='members-announce', holder='NP Richter, Ragnar', contact='Ragnar Richter'),
|
||||
2000068=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='members-discussion', holder='NP Richter, Ragnar', contact='Ragnar Richter'),
|
||||
2000069=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS_ALERT', holder='NP Henning, Eike', contact='Eike Henning'),
|
||||
2000070=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='NP Henning, Eike', contact='Eike Henning'),
|
||||
2000071=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-discussion', holder='NP Henning, Eike', contact='Eike Henning'),
|
||||
2000072=rel(anchor='?? Ragnar IT-Beratung', type='SUBSCRIBER', mark='operations-announce', holder='NP Henning, Eike', contact='Eike Henning'),
|
||||
2000073=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS_ALERT', holder='NP Henning, Jan', contact='Jan Henning'),
|
||||
2000074=rel(anchor='?? Ragnar IT-Beratung', type='OPERATIONS', holder='NP Henning, Jan', contact='Jan Henning')
|
||||
}
|
||||
""");
|
||||
}
|
||||
@ -442,7 +455,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, 1002000, for cancellation D),
|
||||
34003=CoopAssetsTransaction(M-1002000: 2016-12-31, LOSS, -20.00, 1002000, for cancellation D),
|
||||
35001=CoopAssetsTransaction(M-1909000: 2024-01-15, DEPOSIT, 128.00, 1909000, for subscription E),
|
||||
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, ADJUSTMENT, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00),
|
||||
35002=CoopAssetsTransaction(M-1909000: 2024-01-20, REVERSAL, -128.00, 1909000, chargeback for subscription E, M-1909000:DEP:+128.00),
|
||||
358=CoopAssetsTransaction(M-1000300: 2000-12-06, DEPOSIT, 5120, 1000300, for subscription A),
|
||||
442=CoopAssetsTransaction(M-1015200: 2003-07-07, DEPOSIT, 64, 1015200),
|
||||
577=CoopAssetsTransaction(M-1000300: 2011-12-12, DEPOSIT, 1024, 1000300),
|
||||
@ -502,7 +515,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
// this happens if a natural person is marked as 'contractual' for itself
|
||||
final var idsToRemove = new HashSet<Integer>();
|
||||
relations.forEach((id, r) -> {
|
||||
if (r.getHolder() == r.getAnchor()) {
|
||||
if (r.getType() == HsOfficeRelationType.REPRESENTATIVE && r.getHolder() == r.getAnchor()) {
|
||||
idsToRemove.add(id);
|
||||
}
|
||||
});
|
||||
@ -670,7 +683,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
jpaAttempt.transacted(() -> {
|
||||
context(rbacSuperuser);
|
||||
coopShares.forEach(this::persist);
|
||||
updateLegacyIds(coopShares, "hs_office.coopsharestransaction_legacy_id", "member_share_id");
|
||||
updateLegacyIds(coopShares, "hs_office.coopsharetx_legacy_id", "member_share_id");
|
||||
|
||||
}).assertSuccessful();
|
||||
|
||||
@ -795,23 +808,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
? HsOfficeCoopSharesTransactionType.SUBSCRIPTION
|
||||
: "UNSUBSCRIPTION".equals(rec.getString("action"))
|
||||
? HsOfficeCoopSharesTransactionType.CANCELLATION
|
||||
: HsOfficeCoopSharesTransactionType.ADJUSTMENT
|
||||
: HsOfficeCoopSharesTransactionType.REVERSAL
|
||||
)
|
||||
.shareCount(rec.getInteger("quantity"))
|
||||
.comment(rec.getString("comment"))
|
||||
.reference(member.getMemberNumber().toString())
|
||||
.build();
|
||||
|
||||
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.ADJUSTMENT) {
|
||||
if (shareTransaction.getTransactionType() == HsOfficeCoopSharesTransactionType.REVERSAL) {
|
||||
final var negativeValue = -shareTransaction.getShareCount();
|
||||
final var adjustedShareTx = coopShares.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopSharesTransactionType.ADJUSTMENT &&
|
||||
final var revertedShareTx = coopShares.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopSharesTransactionType.REVERSAL &&
|
||||
a.getMembership() == shareTransaction.getMembership() &&
|
||||
a.getShareCount() == negativeValue)
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"cannot determine share reverse entry for adjustment " + shareTransaction));
|
||||
shareTransaction.setAdjustedShareTx(adjustedShareTx);
|
||||
"cannot determine share reverse entry for reversal " + shareTransaction));
|
||||
shareTransaction.setRevertedShareTx(revertedShareTx);
|
||||
}
|
||||
coopShares.put(rec.getInteger("member_share_id"), shareTransaction);
|
||||
});
|
||||
@ -837,7 +850,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
final var assetTypeMapping = new HashMap<String, HsOfficeCoopAssetsTransactionType>() {
|
||||
|
||||
{
|
||||
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.ADJUSTMENT);
|
||||
put("ADJUSTMENT", HsOfficeCoopAssetsTransactionType.REVERSAL);
|
||||
put("HANDOVER", HsOfficeCoopAssetsTransactionType.TRANSFER);
|
||||
put("ADOPTION", HsOfficeCoopAssetsTransactionType.ADOPTION);
|
||||
put("LOSS", HsOfficeCoopAssetsTransactionType.LOSS);
|
||||
@ -865,16 +878,16 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
.reference(member.getMemberNumber().toString())
|
||||
.build();
|
||||
|
||||
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.ADJUSTMENT) {
|
||||
if (assetTransaction.getTransactionType() == HsOfficeCoopAssetsTransactionType.REVERSAL) {
|
||||
final var negativeValue = assetTransaction.getAssetValue().negate();
|
||||
final var adjustedAssetTx = coopAssets.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.ADJUSTMENT &&
|
||||
final var revertedAssetTx = coopAssets.values().stream().filter(a ->
|
||||
a.getTransactionType() != HsOfficeCoopAssetsTransactionType.REVERSAL &&
|
||||
a.getMembership() == assetTransaction.getMembership() &&
|
||||
a.getAssetValue().equals(negativeValue))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"cannot determine asset reverse entry for adjustment " + assetTransaction));
|
||||
assetTransaction.setAdjustedAssetTx(adjustedAssetTx);
|
||||
"cannot determine asset reverse entry for reversal " + assetTransaction));
|
||||
assetTransaction.setRevertedAssetTx(revertedAssetTx);
|
||||
}
|
||||
|
||||
coopAssets.put(rec.getInteger("member_asset_id"), assetTransaction);
|
||||
@ -958,6 +971,9 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
|
||||
HsOfficePersonEntity contactPerson = partnerPerson;
|
||||
if (!StringUtils.equals(rec.getString("firma"), partnerPerson.getTradeName()) ||
|
||||
partnerPerson.getPersonType() != determinePersonType(rec) ||
|
||||
!StringUtils.equals(rec.getString("title"), partnerPerson.getTitle()) ||
|
||||
!StringUtils.equals(rec.getString("salut"), partnerPerson.getSalutation()) ||
|
||||
!StringUtils.equals(rec.getString("first_name"), partnerPerson.getGivenName()) ||
|
||||
!StringUtils.equals(rec.getString("last_name"), partnerPerson.getFamilyName())) {
|
||||
contactPerson = addPerson(HsOfficePersonEntity.builder().build(), rec);
|
||||
@ -976,6 +992,10 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
debitor.getDebitorRel().setContact(contact);
|
||||
}
|
||||
if (containsRole(rec, "operation")) {
|
||||
addRelation(HsOfficeRelationType.OPERATIONS_ALERT, partnerPerson, contactPerson, contact);
|
||||
addRelation(HsOfficeRelationType.OPERATIONS, partnerPerson, contactPerson, contact);
|
||||
}
|
||||
if (containsRole(rec, "silent")) {
|
||||
addRelation(HsOfficeRelationType.OPERATIONS, partnerPerson, contactPerson, contact);
|
||||
}
|
||||
if (containsRole(rec, "contractual")) {
|
||||
@ -1053,34 +1073,60 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
}
|
||||
|
||||
private HsOfficePersonEntity addPerson(final HsOfficePersonEntity person, final Record contactRecord) {
|
||||
// TODO: title+salutation: add to person
|
||||
person.setSalutation(contactRecord.getString("salut"));
|
||||
person.setTitle(contactRecord.getString("title"));
|
||||
person.setGivenName(contactRecord.getString("first_name"));
|
||||
person.setFamilyName(contactRecord.getString("last_name"));
|
||||
person.setTradeName(contactRecord.getString("firma"));
|
||||
determinePersonType(person, contactRecord.getString("roles"));
|
||||
person.setPersonType(determinePersonType(contactRecord));
|
||||
|
||||
persons.put(contactRecord.getInteger("contact_id"), person);
|
||||
return person;
|
||||
}
|
||||
|
||||
private static void determinePersonType(final HsOfficePersonEntity person, final String roles) {
|
||||
if (person.getTradeName().isBlank()) {
|
||||
person.setPersonType(HsOfficePersonType.NATURAL_PERSON);
|
||||
private static HsOfficePersonType determinePersonType(final Record contactRecord) {
|
||||
String roles = contactRecord.getString("roles");
|
||||
String country = contactRecord.getString("country");
|
||||
String familyName = contactRecord.getString("last_name");
|
||||
String givenName = contactRecord.getString("first_name");
|
||||
String tradeName = contactRecord.getString("firma");
|
||||
|
||||
if (PERSON_TYPES_BY_CONTACT.containsKey(contactRecord.getInteger("contact_id"))) {
|
||||
return PERSON_TYPES_BY_CONTACT.get(contactRecord.getInteger("contact_id"));
|
||||
}
|
||||
|
||||
if (tradeName.isBlank() || tradeName.startsWith("verstorben")) {
|
||||
return HsOfficePersonType.NATURAL_PERSON;
|
||||
} else
|
||||
// contractual && !partner with a firm and a natural person name
|
||||
// should actually be split up into two persons
|
||||
// but the legacy database consists such records
|
||||
if (roles.contains("contractual") && !roles.contains("partner") &&
|
||||
!person.getFamilyName().isBlank() && !person.getGivenName().isBlank()) {
|
||||
person.setPersonType(HsOfficePersonType.NATURAL_PERSON);
|
||||
} else if (endsWithWord(person.getTradeName(), "e.K.", "e.G.", "eG", "GmbH", "AG", "KG")) {
|
||||
person.setPersonType(HsOfficePersonType.LEGAL_PERSON);
|
||||
} else if (endsWithWord(person.getTradeName(), "OHG")) {
|
||||
person.setPersonType(HsOfficePersonType.INCORPORATED_FIRM);
|
||||
} else if (endsWithWord(person.getTradeName(), "GbR")) {
|
||||
person.setPersonType(HsOfficePersonType.INCORPORATED_FIRM);
|
||||
|
||||
if (endsWithWord(tradeName, "OHG", "GbR", "KG", "UG", "PartGmbB", "mbB")) {
|
||||
return HsOfficePersonType.INCORPORATED_FIRM; // Personengesellschaft. Gesellschafter haften persönlich.
|
||||
} else if (containsWord(tradeName, "e.K.", "e.G.", "eG", "gGmbH", "GmbH", "mbH", "AG", "e.V.", "eV", "e.V")
|
||||
|| tradeName.toLowerCase().contains("haftungsbeschränkt")
|
||||
|| tradeName.toLowerCase().contains("stiftung")
|
||||
|| tradeName.toLowerCase().contains("stichting")
|
||||
|| tradeName.toLowerCase().contains("foundation")
|
||||
|| tradeName.toLowerCase().contains("schule")
|
||||
|| tradeName.toLowerCase().contains("verein")
|
||||
|| tradeName.toLowerCase().contains("gewerkschaft")
|
||||
|| tradeName.toLowerCase().contains("gesellschaft")
|
||||
|| tradeName.toLowerCase().contains("kirche")
|
||||
|| tradeName.toLowerCase().contains("fraktion")
|
||||
|| tradeName.toLowerCase().contains("landkreis")
|
||||
|| tradeName.toLowerCase().contains("behörde")
|
||||
|| tradeName.toLowerCase().contains("bundesamt")
|
||||
|| tradeName.toLowerCase().contains("bezirksamt")
|
||||
) {
|
||||
return HsOfficePersonType.LEGAL_PERSON; // Haftungsbeschränkt
|
||||
} else if (roles.contains("contractual") && !roles.contains("partner") &&
|
||||
!familyName.isBlank() && !givenName.isBlank()) {
|
||||
// REPRESENTATIVES are always natural persons
|
||||
return HsOfficePersonType.NATURAL_PERSON;
|
||||
} else {
|
||||
person.setPersonType(HsOfficePersonType.UNKNOWN_PERSON_TYPE);
|
||||
return HsOfficePersonType.UNKNOWN_PERSON_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1094,6 +1140,19 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean containsWord(final String value, final String... endings) {
|
||||
final var lowerCaseValue = value.toLowerCase();
|
||||
for (String ending : endings) {
|
||||
if (lowerCaseValue.equals(ending.toLowerCase()) ||
|
||||
lowerCaseValue.startsWith(ending.toLowerCase() + " ") ||
|
||||
lowerCaseValue.contains(" " + ending.toLowerCase() + " ") ||
|
||||
lowerCaseValue.endsWith(" " + ending.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void verifyContainsOnlyKnownRoles(final String roles) {
|
||||
final var allowedRolesSet = stream(KNOWN_ROLES).collect(Collectors.toSet());
|
||||
final var givenRolesSet = stream(roles.replace(" ", "").split(",")).collect(Collectors.toSet());
|
||||
@ -1111,7 +1170,7 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
contactRecord.getString("last_name"),
|
||||
contactRecord.getString("firma")));
|
||||
contact.putEmailAddresses(Map.of("main", contactRecord.getString("email")));
|
||||
contact.setPostalAddress(toAddress(contactRecord));
|
||||
contact.putPostalAddress(toAddress(contactRecord));
|
||||
contact.putPhoneNumbers(toPhoneNumbers(contactRecord));
|
||||
|
||||
contacts.put(contactRecord.getInteger("contact_id"), contact);
|
||||
@ -1131,36 +1190,23 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
private String toAddress(final Record rec) {
|
||||
final var result = new StringBuilder();
|
||||
private Map<String, String> toAddress(final Record rec) {
|
||||
final var result = new LinkedHashMap<String, String>();
|
||||
final var name = toName(
|
||||
rec.getString("salut"),
|
||||
rec.getString("title"),
|
||||
rec.getString("first_name"),
|
||||
rec.getString("last_name"));
|
||||
if (isNotBlank(name))
|
||||
result.append(name + "\n");
|
||||
result.put("name", name);
|
||||
if (isNotBlank(rec.getString("firma")))
|
||||
result.append(rec.getString("firma") + "\n");
|
||||
if (isNotBlank(rec.getString("co")))
|
||||
result.append("c/o " + rec.getString("co") + "\n");
|
||||
if (isNotBlank(rec.getString("street")))
|
||||
result.append(rec.getString("street") + "\n");
|
||||
final var zipcodeAndCity = toZipcodeAndCity(rec);
|
||||
if (isNotBlank(zipcodeAndCity))
|
||||
result.append(zipcodeAndCity + "\n");
|
||||
return result.toString();
|
||||
}
|
||||
result.put("firm", name);
|
||||
|
||||
private String toZipcodeAndCity(final Record rec) {
|
||||
final var result = new StringBuilder();
|
||||
if (isNotBlank(rec.getString("country")))
|
||||
result.append(rec.getString("country") + " ");
|
||||
if (isNotBlank(rec.getString("zipcode")))
|
||||
result.append(rec.getString("zipcode") + " ");
|
||||
if (isNotBlank(rec.getString("city")))
|
||||
result.append(rec.getString("city"));
|
||||
return result.toString();
|
||||
List.of("co", "street", "zipcode", "city", "country").forEach(key -> {
|
||||
if (isNotBlank(rec.getString(key)))
|
||||
result.put(key, rec.getString(key));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private String toCaption(
|
||||
@ -1171,13 +1217,13 @@ public abstract class BaseOfficeDataImport extends CsvDataImport {
|
||||
final String firm) {
|
||||
final var result = new StringBuilder();
|
||||
if (isNotBlank(salut))
|
||||
result.append(salut + " ");
|
||||
result.append((isBlank(result) ? "" : " ") + salut);
|
||||
if (isNotBlank(title))
|
||||
result.append(title + " ");
|
||||
result.append((isBlank(result) ? "" : " ") + title);
|
||||
if (isNotBlank(firstname))
|
||||
result.append(firstname + " ");
|
||||
result.append((isBlank(result) ? "" : " ") + firstname);
|
||||
if (isNotBlank(lastname))
|
||||
result.append(lastname + " ");
|
||||
result.append((isBlank(result) ? "" : " ") + lastname);
|
||||
if (isNotBlank(firm)) {
|
||||
result.append((isBlank(result) ? "" : ", ") + firm);
|
||||
}
|
||||
|
@ -255,7 +255,7 @@ public class CsvDataImport extends ContextBasedTest {
|
||||
em.createNativeQuery("delete from hs_office.coopassettx where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.coopassettx_legacy_id where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.coopsharetx where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.coopsharestransaction_legacy_id where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.coopsharetx_legacy_id where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.membership where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.sepamandate where true").executeUpdate();
|
||||
em.createNativeQuery("delete from hs_office.sepamandate_legacy_id where true").executeUpdate();
|
||||
@ -275,7 +275,7 @@ public class CsvDataImport extends ContextBasedTest {
|
||||
em.createNativeQuery("alter sequence hs_office.contact_legacy_id_seq restart with 1000000000;").executeUpdate();
|
||||
em.createNativeQuery("alter sequence hs_office.coopassettx_legacy_id_seq restart with 1000000000;")
|
||||
.executeUpdate();
|
||||
em.createNativeQuery("alter sequence public.hs_office.coopsharestransaction_legacy_id_seq restart with 1000000000;")
|
||||
em.createNativeQuery("alter sequence public.hs_office.coopsharetx_legacy_id_seq restart with 1000000000;")
|
||||
.executeUpdate();
|
||||
em.createNativeQuery("alter sequence public.hs_office.partner_legacy_id_seq restart with 1000000000;")
|
||||
.executeUpdate();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user