hosting-asset-data-migration (#79)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/79 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
		
							
								
								
									
										18
									
								
								.aliases
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								.aliases
									
									
									
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| # For using the alias import-office-tables, | # For using the alias gw-importOfficeData or gw-importHostingAssets, | ||||||
| # copy the file .tc-environment to .environment (ignored by git) | # copy the file .tc-environment to .environment (ignored by git) | ||||||
| # and amend them according to your external DB. | # and amend them according to your external DB. | ||||||
|  |  | ||||||
| @@ -42,7 +42,11 @@ postgresAutodoc () { | |||||||
| } | } | ||||||
| alias postgres-autodoc=postgresAutodoc | alias postgres-autodoc=postgresAutodoc | ||||||
|  |  | ||||||
| function importOfficeData() { | function importLegacyData() { | ||||||
|  |     export target=$1 | ||||||
|  |     if [ -z "$target" ]; then | ||||||
|  |         echo "importLegacyData needs target argument, but none was given" >&2 | ||||||
|  |     else | ||||||
|         source .tc-environment |         source .tc-environment | ||||||
|  |  | ||||||
|         if [ -f .environment ]; then |         if [ -f .environment ]; then | ||||||
| @@ -50,11 +54,17 @@ function importOfficeData() { | |||||||
|         fi |         fi | ||||||
|  |  | ||||||
|         echo "using environment (with ending ';' for use in IntelliJ IDEA):" |         echo "using environment (with ending ';' for use in IntelliJ IDEA):" | ||||||
|  |         echo "--- BEGIN: ---" | ||||||
|         set | grep ^HSADMINNG_ | sed 's/$/;/' |         set | grep ^HSADMINNG_ | sed 's/$/;/' | ||||||
|  |         echo "---- END. ----" | ||||||
|  |         echo | ||||||
|  |  | ||||||
| 	./gradlew importOfficeData --rerun |         echo ./gradlew $target --rerun | ||||||
|  |         ./gradlew $target --rerun | ||||||
|  |     fi | ||||||
| } | } | ||||||
| alias gw-importOfficeData=importOfficeData | alias gw-importOfficeData='importLegacyData importOfficeData' | ||||||
|  | alias gw-importHostingAssets='importLegacyData importHostingAssets' | ||||||
|  |  | ||||||
| alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' | alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' | ||||||
| alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' | alias podman-stop='systemctl --user disable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock' | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -136,4 +136,9 @@ Desktop.ini | |||||||
| # ESLint | # ESLint | ||||||
| ###################### | ###################### | ||||||
| .eslintcache | .eslintcache | ||||||
|  |  | ||||||
|  | ###################### | ||||||
|  | # Project Related | ||||||
|  | ###################### | ||||||
| /.environment* | /.environment* | ||||||
|  | /src/test/resources/migration-prod/* | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								build.gradle
									
									
									
									
									
								
							| @@ -318,7 +318,7 @@ jacocoTestCoverageVerification { | |||||||
|  |  | ||||||
| tasks.register('importOfficeData', Test) { | tasks.register('importOfficeData', Test) { | ||||||
|     useJUnitPlatform { |     useJUnitPlatform { | ||||||
|         includeTags 'import' |         includeTags 'importOfficeData' | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     group 'verification' |     group 'verification' | ||||||
| @@ -327,6 +327,16 @@ tasks.register('importOfficeData', Test) { | |||||||
|     mustRunAfter spotlessJava |     mustRunAfter spotlessJava | ||||||
| } | } | ||||||
|  |  | ||||||
|  | tasks.register('importHostingAssets', Test) { | ||||||
|  |     useJUnitPlatform { | ||||||
|  |         includeTags 'importHostingAssets' | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     group 'verification' | ||||||
|  |     description 'run the import jobs as tests' | ||||||
|  |  | ||||||
|  |     mustRunAfter spotlessJava | ||||||
|  | } | ||||||
|  |  | ||||||
| // pitest mutation testing | // pitest mutation testing | ||||||
| pitest { | pitest { | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ import static net.hostsharing.hsadminng.stringify.Stringify.stringify; | |||||||
|  |  | ||||||
| // a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity | // a partial HsOfficeDebitorEntity to reduce the number of SQL queries to load the entity | ||||||
| @Entity | @Entity | ||||||
| @Table(name = "hs_booking_debitor_rv") | @Table(name = "hs_booking_debitor_xv") | ||||||
| @Getter | @Getter | ||||||
| @Builder | @Builder | ||||||
| @NoArgsConstructor | @NoArgsConstructor | ||||||
|   | |||||||
| @@ -184,7 +184,9 @@ public class HsBookingItemEntity implements Stringifyable, RbacObject, Propertie | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     public HsBookingProjectEntity getRelatedProject() { |     public HsBookingProjectEntity getRelatedProject() { | ||||||
|         return project != null ? project : parentItem.getRelatedProject(); |         return project != null ? project | ||||||
|  |                 : parentItem != null ? parentItem.getRelatedProject() | ||||||
|  |                 : null; // can be the case for technical assets like IP-numbers | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static RbacView rbac() { |     public static RbacView rbac() { | ||||||
|   | |||||||
| @@ -22,6 +22,10 @@ public class HsBookingItemEntityValidator extends HsEntityValidator<HsBookingIte | |||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public List<String> validateEntity(final HsBookingItemEntity bookingItem) { |     public List<String> validateEntity(final HsBookingItemEntity bookingItem) { | ||||||
|  |         // TODO.impl: HsBookingItemType could do this similar to HsHostingAssetType | ||||||
|  |         if ( bookingItem.getParentItem() == null && bookingItem.getProject() == null) { | ||||||
|  |             return List.of(bookingItem + ".'parentItem' or .'project' expected to be set, but both are null"); | ||||||
|  |         } | ||||||
|         return enrich(prefix(bookingItem.toShortString(), "resources"), super.validateProperties(bookingItem)); |         return enrich(prefix(bookingItem.toShortString(), "resources"), super.validateProperties(bookingItem)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,11 +11,12 @@ class HsCloudServerBookingItemValidator extends HsBookingItemEntityValidator { | |||||||
|             // @formatter:off |             // @formatter:off | ||||||
|             booleanProperty("active")                                       .withDefault(true), |             booleanProperty("active")                                       .withDefault(true), | ||||||
|  |  | ||||||
|             integerProperty("CPUs")                 .min(  1)   .max(   32) .required(), |             integerProperty("CPU")                  .min(  1)   .max(   32) .required(), | ||||||
|             integerProperty("RAM").unit("GB")       .min(  1)   .max(  128) .required(), |             integerProperty("RAM").unit("GB")       .min(  1)   .max( 8192) .required(), | ||||||
|             integerProperty("SSD").unit("GB")       .min(  0)   .max( 1000) .step(25).required(), // (1) |             integerProperty("SSD").unit("GB")       .min( 25)   .max( 1000) .step(25).requiresAtLeastOneOf("SDD", "HDD"), | ||||||
|             integerProperty("HDD").unit("GB")       .min(  0)   .max( 4000) .step(250).withDefault(0), |             integerProperty("HDD").unit("GB")       .min(250)   .max( 4000) .step(250).requiresAtLeastOneOf("SSD", "HDD"), | ||||||
|             integerProperty("Traffic").unit("GB")   .min(250)   .max(10000) .step(250).required(), |             integerProperty("Traffic").unit("GB")   .min(250)   .max(10000) .step(250).requiresAtMaxOneOf("Bandwidth", "Traffic"), | ||||||
|  |             integerProperty("Bandwidth").unit("GB") .min(250)   .max(10000) .step(250).requiresAtMaxOneOf("Bandwidth", "Traffic"), // TODO.spec | ||||||
|  |  | ||||||
|             enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional() |             enumerationProperty("SLA-Infrastructure").values("BASIC", "EXT8H", "EXT4H", "EXT2H").optional() | ||||||
|             // @formatter:on |             // @formatter:on | ||||||
|   | |||||||
| @@ -10,11 +10,12 @@ class HsManagedServerBookingItemValidator extends HsBookingItemEntityValidator { | |||||||
|  |  | ||||||
|     HsManagedServerBookingItemValidator() { |     HsManagedServerBookingItemValidator() { | ||||||
|         super( |         super( | ||||||
|             integerProperty("CPUs").min(1).max(32).required(), |             integerProperty("CPU").min(1).max(32).required(), | ||||||
|             integerProperty("RAM").unit("GB").min(1).max(128).required(), |             integerProperty("RAM").unit("GB").min(1).max(128).required(), | ||||||
|             integerProperty("SSD").unit("GB").min(25).max(1000).step(25).required().asTotalLimit().withThreshold(200), |             integerProperty("SSD").unit("GB").min(25).max(2000).step(25).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit().withThreshold(200), | ||||||
|             integerProperty("HDD").unit("GB").min(0).max(4000).step(250).withDefault(0).asTotalLimit().withThreshold(200), |             integerProperty("HDD").unit("GB").min(250).max(10000).step(250).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit().withThreshold(200), | ||||||
|             integerProperty("Traffic").unit("GB").min(250).max(10000).step(250).required().asTotalLimit().withThreshold(200), |             integerProperty("Traffic").unit("GB").min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit().withThreshold(200), | ||||||
|  |             integerProperty("Bandwidth").unit("GB").min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit().withThreshold(200), // TODO.spec | ||||||
|             enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").withDefault("BASIC"), |             enumerationProperty("SLA-Platform").values("BASIC", "EXT8H", "EXT4H", "EXT2H").withDefault("BASIC"), | ||||||
|             booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").withDefault(false), |             booleanProperty("SLA-EMail").falseIf("SLA-Platform", "BASIC").withDefault(false), | ||||||
|             booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(), |             booleanProperty("SLA-Maria").falseIf("SLA-Platform", "BASIC").optional(), | ||||||
|   | |||||||
| @@ -23,16 +23,17 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator | |||||||
|  |  | ||||||
|     public HsManagedWebspaceBookingItemValidator() { |     public HsManagedWebspaceBookingItemValidator() { | ||||||
|         super( |         super( | ||||||
|             integerProperty("SSD").unit("GB").min(1).max(100).step(1).required(), |             integerProperty("SSD").unit("GB").min(1).max(2000).step(1).required(), | ||||||
|             integerProperty("HDD").unit("GB").min(0).max(250).step(10).optional(), |             integerProperty("HDD").unit("GB").min(0).max(10000).step(10).optional(), | ||||||
|             integerProperty("Traffic").unit("GB").min(10).max(1000).step(10).required(), |             integerProperty("Traffic").unit("GB").min(10).max(64000).step(10).requiresAtMaxOneOf("Bandwidth", "Traffic"), | ||||||
|  |             integerProperty("Bandwidth").unit("GB").min(10).max(1000).step(10).requiresAtMaxOneOf("Bandwidth", "Traffic"), // TODO.spec | ||||||
|             integerProperty("Multi").min(1).max(100).step(1).withDefault(1) |             integerProperty("Multi").min(1).max(100).step(1).withDefault(1) | ||||||
|                     .eachComprising( 25, unixUsers()) |                     .eachComprising( 25, unixUsers()) | ||||||
|                     .eachComprising(  5, databaseUsers()) |                     .eachComprising(  5, databaseUsers()) | ||||||
|                     .eachComprising(  5, databases()) |                     .eachComprising(  5, databases()) | ||||||
|                     .eachComprising(250, eMailAddresses()), |                     .eachComprising(250, eMailAddresses()), | ||||||
|             integerProperty("Daemons").min(0).max(10).withDefault(0), |             integerProperty("Daemons").min(0).max(16).withDefault(0), | ||||||
|             booleanProperty("Online Office Server").optional(), |             booleanProperty("Online Office Server").optional(), // TODO.impl: shorten to "Office" | ||||||
|             enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").withDefault("BASIC") |             enumerationProperty("SLA-Platform").values("BASIC", "EXT24H").withDefault("BASIC") | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -7,15 +7,16 @@ class HsPrivateCloudBookingItemValidator extends HsBookingItemEntityValidator { | |||||||
|     HsPrivateCloudBookingItemValidator() { |     HsPrivateCloudBookingItemValidator() { | ||||||
|         super( |         super( | ||||||
|             // @formatter:off |             // @formatter:off | ||||||
|             integerProperty("CPUs")                     .min(  1).max(  128).required().asTotalLimit(), |             integerProperty("CPU")                     .min(  1).max(  128).required().asTotalLimit(), | ||||||
|             integerProperty("RAM").unit("GB")           .min(  1).max(  512).required().asTotalLimit(), |             integerProperty("RAM").unit("GB")           .min(  1).max(  512).required().asTotalLimit(), | ||||||
|             integerProperty("SSD").unit("GB")           .min( 25).max( 4000).step(25).required().asTotalLimit(), |             integerProperty("SSD").unit("GB")           .min( 25).max( 4000).step(25).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit(), | ||||||
|             integerProperty("HDD").unit("GB")           .min(  0).max(16000).step(250).withDefault(0).asTotalLimit(), |             integerProperty("HDD").unit("GB")           .min(250).max(16000).step(250).requiresAtLeastOneOf("SSD", "HDD").asTotalLimit(), | ||||||
|             integerProperty("Traffic").unit("GB")       .min(250).max(40000).step(250).required().asTotalLimit(), |             integerProperty("Traffic").unit("GB")       .min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit(), | ||||||
|  |             integerProperty("Bandwidth").unit("GB")     .min(250).max(64000).step(250).requiresAtMaxOneOf("Bandwidth", "Traffic").asTotalLimit(), // TODO.spec | ||||||
|  |  | ||||||
| //          Alternatively we could specify it similarly to "Multi" option but exclusively counting: | //          Alternatively we could specify it similarly to "Multi" option but exclusively counting: | ||||||
| //          integerProperty("Resource-Points")          .min(4).max(100).required() | //          integerProperty("Resource-Points")          .min(4).max(100).required() | ||||||
| //                  .each("CPUs").countsAs(64) | //                  .each("CPU").countsAs(64) | ||||||
| //                  .each("RAM").countsAs(64) | //                  .each("RAM").countsAs(64) | ||||||
| //                  .each("SSD").countsAs(18) | //                  .each("SSD").countsAs(18) | ||||||
| //                  .each("HDD").countsAs(2) | //                  .each("HDD").countsAs(2) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import lombok.Getter; | |||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| import lombok.Setter; | import lombok.Setter; | ||||||
| import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; | import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; | ||||||
| import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; | import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; | ||||||
| import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; | import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; | ||||||
| import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; | import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; | ||||||
| @@ -38,6 +39,7 @@ import java.io.IOException; | |||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  | import java.util.Optional; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
|  |  | ||||||
| import static java.util.Collections.emptyMap; | import static java.util.Collections.emptyMap; | ||||||
| @@ -134,6 +136,14 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti | |||||||
|         this.isLoaded = true; |         this.isLoaded = true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public HsBookingProjectEntity getRelatedProject() { | ||||||
|  |         return Optional.ofNullable(bookingItem) | ||||||
|  |                 .map(HsBookingItemEntity::getRelatedProject) | ||||||
|  |                 .orElseGet(() -> Optional.ofNullable(parentAsset) | ||||||
|  |                         .map(HsHostingAssetEntity::getRelatedProject) | ||||||
|  |                         .orElse(null)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     public PatchableMapWrapper<Object> getConfig() { |     public PatchableMapWrapper<Object> getConfig() { | ||||||
|         return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); |         return PatchableMapWrapper.of(configWrapper, (newWrapper) -> {configWrapper = newWrapper;}, config); | ||||||
|     } |     } | ||||||
| @@ -163,7 +173,6 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti | |||||||
|         return emptyMap(); |         return emptyMap(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public String toString() { |     public String toString() { | ||||||
|         return stringify.apply(this); |         return stringify.apply(this); | ||||||
| @@ -202,7 +211,8 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti | |||||||
|                         directlyFetchedByDependsOnColumn(), |                         directlyFetchedByDependsOnColumn(), | ||||||
|                         NULLABLE) |                         NULLABLE) | ||||||
|  |  | ||||||
|                 .switchOnColumn("type", |                 .switchOnColumn( | ||||||
|  |                         "type", | ||||||
|                         inCaseOf("DOMAIN_SETUP", then -> { |                         inCaseOf("DOMAIN_SETUP", then -> { | ||||||
|                             then.toRole(GLOBAL, GUEST).grantPermission(INSERT); |                             then.toRole(GLOBAL, GUEST).grantPermission(INSERT); | ||||||
|                         }) |                         }) | ||||||
| @@ -231,7 +241,14 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti | |||||||
|                     with.permission(SELECT); |                     with.permission(SELECT); | ||||||
|                 }) |                 }) | ||||||
|  |  | ||||||
|                 .limitDiagramTo("asset", "bookingItem", "bookingItem.debitorRel", "parentAsset", "assignedToAsset", "alarmContact", "global"); |                 .limitDiagramTo( | ||||||
|  |                         "asset", | ||||||
|  |                         "bookingItem", | ||||||
|  |                         "bookingItem.debitorRel", | ||||||
|  |                         "parentAsset", | ||||||
|  |                         "assignedToAsset", | ||||||
|  |                         "alarmContact", | ||||||
|  |                         "global"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static void main(String[] args) throws IOException { |     public static void main(String[] args) throws IOException { | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator | |||||||
|     protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { |     protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) { | ||||||
|         final var prefixPattern = |         final var prefixPattern = | ||||||
|                 !assetEntity.isLoaded() |                 !assetEntity.isLoaded() | ||||||
|                         ? assetEntity.getParentAsset().getBookingItem().getProject().getDebitor().getDefaultPrefix() |                         ? assetEntity.getRelatedProject().getDebitor().getDefaultPrefix() | ||||||
|                         : "[a-z][a-z0-9][a-z0-9]"; |                         : "[a-z][a-z0-9][a-z0-9]"; | ||||||
|         return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$"); |         return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -68,7 +68,7 @@ public class HsOfficeMembershipEntity implements RbacObject, Stringifyable { | |||||||
|  |  | ||||||
|     private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class) |     private static Stringify<HsOfficeMembershipEntity> stringify = stringify(HsOfficeMembershipEntity.class) | ||||||
|             .withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber()) |             .withProp(e -> MEMBER_NUMBER_TAG + e.getMemberNumber()) | ||||||
|             .withProp(e -> e.getPartner().toShortString()) |             .withProp(HsOfficeMembershipEntity::getPartner) | ||||||
|             .withProp(e -> e.getValidity().asString()) |             .withProp(e -> e.getValidity().asString()) | ||||||
|             .withProp(HsOfficeMembershipEntity::getStatus) |             .withProp(HsOfficeMembershipEntity::getStatus) | ||||||
|             .quotedValues(false); |             .quotedValues(false); | ||||||
|   | |||||||
| @@ -13,10 +13,12 @@ import java.util.ArrayList; | |||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
|  | import java.util.LinkedHashSet; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
|  | import java.util.Set; | ||||||
| import java.util.function.Function; | import java.util.function.Function; | ||||||
|  |  | ||||||
| import static java.lang.Boolean.FALSE; | import static java.lang.Boolean.FALSE; | ||||||
| @@ -30,7 +32,7 @@ import static org.apache.commons.lang3.ObjectUtils.isArray; | |||||||
| public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T> { | public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T> { | ||||||
|  |  | ||||||
|     protected static final String[] KEY_ORDER_HEAD = Array.of("propertyName"); |     protected static final String[] KEY_ORDER_HEAD = Array.of("propertyName"); | ||||||
|     protected static final String[] KEY_ORDER_TAIL = Array.of("required", "defaultValue", "readOnly", "writeOnly", "computed", "isTotalsValidator", "thresholdPercentage"); |     protected static final String[] KEY_ORDER_TAIL = Array.of("required", "requiresAtLeastOneOf", "requiresAtMaxOneOf", "defaultValue", "readOnly", "writeOnly", "computed", "isTotalsValidator", "thresholdPercentage"); | ||||||
|     protected static final String[] KEY_ORDER = Array.join(KEY_ORDER_HEAD, KEY_ORDER_TAIL); |     protected static final String[] KEY_ORDER = Array.join(KEY_ORDER_HEAD, KEY_ORDER_TAIL); | ||||||
|  |  | ||||||
|     final Class<T> type; |     final Class<T> type; | ||||||
| @@ -40,6 +42,8 @@ public abstract class ValidatableProperty<P extends ValidatableProperty<?, ?>, T | |||||||
|     private final String[] keyOrder; |     private final String[] keyOrder; | ||||||
|  |  | ||||||
|     private Boolean required; |     private Boolean required; | ||||||
|  |     private Set<String> requiresAtLeastOneOf; | ||||||
|  |     private Set<String> requiresAtMaxOneOf; | ||||||
|     private T defaultValue; |     private T defaultValue; | ||||||
|  |  | ||||||
|     @JsonIgnore |     @JsonIgnore | ||||||
| @@ -100,9 +104,19 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]> | |||||||
|         return self(); |         return self(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public ValidatableProperty<P, T> optional() { |     public P optional() { | ||||||
|         required = FALSE; |         required = FALSE; | ||||||
|         return this; |         return self(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public P requiresAtLeastOneOf(final String... propNames) { | ||||||
|  |         requiresAtLeastOneOf = new LinkedHashSet<>(List.of(propNames)); | ||||||
|  |         return self(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public P requiresAtMaxOneOf(final String... propNames) { | ||||||
|  |         requiresAtMaxOneOf = new LinkedHashSet<>(List.of(propNames)); | ||||||
|  |         return self(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public P withDefault(final T value) { |     public P withDefault(final T value) { | ||||||
| @@ -172,28 +186,57 @@ protected void setDeferredInit(final Function<ValidatableProperty<?, ?>[], T[]> | |||||||
|         final var result = new ArrayList<String>(); |         final var result = new ArrayList<String>(); | ||||||
|         final var props = propsProvider.directProps(); |         final var props = propsProvider.directProps(); | ||||||
|         final var propValue = props.get(propertyName); |         final var propValue = props.get(propertyName); | ||||||
|  |  | ||||||
|         if (propValue == null) { |         if (propValue == null) { | ||||||
|             if (required) { |             if (required == TRUE) { | ||||||
|                 result.add(propertyName + "' is required but missing"); |                 result.add(propertyName + "' is required but missing"); | ||||||
|             } |             } | ||||||
|  |             validateRequiresAtLeastOneOf(result, propsProvider); | ||||||
|         } |         } | ||||||
|         if (propValue != null){ |         if (propValue != null){ | ||||||
|  |             validateRequiresAtMaxOneOf(result, propsProvider); | ||||||
|  |  | ||||||
|             if ( type.isInstance(propValue)) { |             if ( type.isInstance(propValue)) { | ||||||
|                 //noinspection unchecked |                 //noinspection unchecked | ||||||
|                 validate(result, (T) propValue, propsProvider); |                 validate(result, (T) propValue, propsProvider); | ||||||
|             } else { |             } else { | ||||||
|                 result.add(propertyName + "' is expected to be of type " + type.getSimpleName() + ", " + |                 result.add(propertyName + "' is expected to be of type " + type.getSimpleName() + ", " + | ||||||
|                         "but is of type " + propValue.getClass().getSimpleName() + ""); |                         "but is of type " + propValue.getClass().getSimpleName()); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private void validateRequiresAtLeastOneOf(final ArrayList<String> result, final PropertiesProvider propsProvider) { | ||||||
|  |         if (requiresAtLeastOneOf != null ) { | ||||||
|  |             final var allPropNames = propsProvider.directProps().keySet(); | ||||||
|  |             final var entriesWithValue = allPropNames.stream() | ||||||
|  |                     .filter(name -> requiresAtLeastOneOf.contains(name)) | ||||||
|  |                     .count(); | ||||||
|  |             if (entriesWithValue == 0) { | ||||||
|  |                 result.add(propertyName + "' is required once in group " + requiresAtLeastOneOf + " but missing"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void validateRequiresAtMaxOneOf(final ArrayList<String> result, final PropertiesProvider propsProvider) { | ||||||
|  |         if (requiresAtMaxOneOf != null) { | ||||||
|  |             final var allPropNames = propsProvider.directProps().keySet(); | ||||||
|  |             final var entriesWithValue = allPropNames.stream() | ||||||
|  |                     .filter(name -> requiresAtMaxOneOf.contains(name)) | ||||||
|  |                     .count(); | ||||||
|  |             if (entriesWithValue > 1) { | ||||||
|  |                 result.add(propertyName + "' is required at max once in group " + requiresAtMaxOneOf | ||||||
|  |                         + " but multiple properties are set"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     protected abstract void validate(final List<String> result, final T propValue, final PropertiesProvider propProvider); |     protected abstract void validate(final List<String> result, final T propValue, final PropertiesProvider propProvider); | ||||||
|  |  | ||||||
|     public void verifyConsistency(final Map.Entry<? extends Enum<?>, ?> typeDef) { |     public void verifyConsistency(final Map.Entry<? extends Enum<?>, ?> typeDef) { | ||||||
|         if (required == null ) { |         if (required == null && requiresAtLeastOneOf == null && requiresAtMaxOneOf == null) { | ||||||
|             throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required() or .optional()" ); |             throw new IllegalStateException(typeDef.getKey() + "[" + propertyName + "] not fully initialized, please call either .required(), .optional(), .withDefault(...), .requiresAtLeastOneOf(...) or .requiresAtMaxOneOf(...)" ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,12 +4,12 @@ | |||||||
| --changeset hs-booking-debitor-RESTRICTED-VIEW:1 endDelimiter:--// | --changeset hs-booking-debitor-RESTRICTED-VIEW:1 endDelimiter:--// | ||||||
| -- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| create view hs_booking_debitor_rv as | create view hs_booking_debitor_xv as | ||||||
|     select debitor.uuid, |     select debitor.uuid, | ||||||
|            debitor.version, |            debitor.version, | ||||||
|            (partner.partnerNumber::varchar || debitor.debitorNumberSuffix)::numeric as debitorNumber, |            (partner.partnerNumber::varchar || debitor.debitorNumberSuffix)::numeric as debitorNumber, | ||||||
|            debitor.defaultPrefix |            debitor.defaultPrefix | ||||||
|     from hs_office_debitor_rv debitor |     from hs_office_debitor debitor | ||||||
|      -- RBAC for debitor is sufficient, for faster access we are bypassing RBAC for the join tables |      -- RBAC for debitor is sufficient, for faster access we are bypassing RBAC for the join tables | ||||||
|     join hs_office_relation debitorRel on debitor.debitorReluUid=debitorRel.uuid |     join hs_office_relation debitorRel on debitor.debitorReluUid=debitorRel.uuid | ||||||
|     join hs_office_relation partnerRel on partnerRel.holderUuid=debitorRel.anchorUuid |     join hs_office_relation partnerRel on partnerRel.holderUuid=debitorRel.anchorUuid | ||||||
|   | |||||||
| @@ -33,11 +33,11 @@ begin | |||||||
|     managedServerUuid := uuid_generate_v4(); |     managedServerUuid := uuid_generate_v4(); | ||||||
|     insert |     insert | ||||||
|         into hs_booking_item (uuid, projectuuid,            type,               parentitemuuid,     caption,                    validity,                           resources) |         into hs_booking_item (uuid, projectuuid,            type,               parentitemuuid,     caption,                    validity,                           resources) | ||||||
|         values (privateCloudUuid,   relatedProject.uuid,    'PRIVATE_CLOUD',    null,               'some PrivateCloud',        daterange('20240401', null, '[]'),  '{ "CPUs": 10, "RAM": 32, "SSD": 4000, "HDD": 10000, "Traffic": 2000 }'::jsonb), |         values (privateCloudUuid,   relatedProject.uuid,    'PRIVATE_CLOUD',    null,               'some PrivateCloud',        daterange('20240401', null, '[]'),  '{ "CPU": 10, "RAM": 32, "SSD": 4000, "HDD": 10000, "Traffic": 2000 }'::jsonb), | ||||||
|                (uuid_generate_v4(), null,                   'MANAGED_SERVER',   privateCloudUuid,   'some ManagedServer',       daterange('20230115', '20240415',   '[)'), '{ "CPUs": 2, "RAM": 4, "SSD": 500, "Traffic": 500 }'::jsonb), |                (uuid_generate_v4(), null,                   'MANAGED_SERVER',   privateCloudUuid,   'some ManagedServer',       daterange('20230115', '20240415',   '[)'), '{ "CPU": 2, "RAM": 4, "SSD": 500, "Traffic": 500 }'::jsonb), | ||||||
|                (uuid_generate_v4(), null,                   'CLOUD_SERVER',     privateCloudUuid,   'test CloudServer',         daterange('20230115', '20240415',   '[)'), '{ "CPUs": 2, "RAM": 4, "SSD": 750, "Traffic": 500 }'::jsonb), |                (uuid_generate_v4(), null,                   'CLOUD_SERVER',     privateCloudUuid,   'test CloudServer',         daterange('20230115', '20240415',   '[)'), '{ "CPU": 2, "RAM": 4, "SSD": 750, "Traffic": 500 }'::jsonb), | ||||||
|                (uuid_generate_v4(), null,                   'CLOUD_SERVER',     privateCloudUuid,   'prod CloudServer',         daterange('20230115', '20240415',   '[)'), '{ "CPUs": 4, "RAM": 16, "SSD": 1000, "Traffic": 500 }'::jsonb), |                (uuid_generate_v4(), null,                   'CLOUD_SERVER',     privateCloudUuid,   'prod CloudServer',         daterange('20230115', '20240415',   '[)'), '{ "CPU": 4, "RAM": 16, "SSD": 1000, "Traffic": 500 }'::jsonb), | ||||||
|                (managedServerUuid,  relatedProject.uuid,    'MANAGED_SERVER',   null,               'separate ManagedServer',   daterange('20221001', null, '[]'),  '{ "CPUs": 2, "RAM": 8, "SSD": 500, "Traffic": 500 }'::jsonb), |                (managedServerUuid,  relatedProject.uuid,    'MANAGED_SERVER',   null,               'separate ManagedServer',   daterange('20221001', null, '[]'),  '{ "CPU": 2, "RAM": 8, "SSD": 500, "Traffic": 500 }'::jsonb), | ||||||
|                (uuid_generate_v4(), null,                   'MANAGED_WEBSPACE', managedServerUuid,  'some ManagedWebspace',     daterange('20221001', null, '[]'),  '{ "SSD": 50, "Traffic": 20, "Daemons": 2, "Multi": 4 }'::jsonb), |                (uuid_generate_v4(), null,                   'MANAGED_WEBSPACE', managedServerUuid,  'some ManagedWebspace',     daterange('20221001', null, '[]'),  '{ "SSD": 50, "Traffic": 20, "Daemons": 2, "Multi": 4 }'::jsonb), | ||||||
|                (uuid_generate_v4(), relatedProject.uuid,    'MANAGED_WEBSPACE', null,               'separate ManagedWebspace', daterange('20221001', null, '[]'),  '{ "SSD": 100, "Traffic": 50, "Daemons": 0, "Multi": 1 }'::jsonb); |                (uuid_generate_v4(), relatedProject.uuid,    'MANAGED_WEBSPACE', null,               'separate ManagedWebspace', daterange('20221001', null, '[]'),  '{ "SSD": 100, "Traffic": 50, "Daemons": 0, "Multi": 1 }'::jsonb); | ||||||
| end; $$; | end; $$; | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ create table if not exists hs_hosting_asset | |||||||
|     alarmContactUuid    uuid null references hs_office_contact(uuid) initially deferred, |     alarmContactUuid    uuid null references hs_office_contact(uuid) initially deferred, | ||||||
|  |  | ||||||
|     constraint chk_hs_hosting_asset_has_booking_item_or_parent_asset |     constraint chk_hs_hosting_asset_has_booking_item_or_parent_asset | ||||||
|         check (bookingItemUuid is not null or parentAssetUuid is not null or type='DOMAIN_SETUP') |         check (bookingItemUuid is not null or parentAssetUuid is not null or type in ('DOMAIN_SETUP', 'IPV4_NUMBER', 'IPV6_NUMBER')) | ||||||
| ); | ); | ||||||
| --// | --// | ||||||
|  |  | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.coopshares", |                     "..hs.office.coopshares", | ||||||
|                     "..hs.office.debitor", |                     "..hs.office.debitor", | ||||||
|                     "..hs.office.membership", |                     "..hs.office.membership", | ||||||
|                     "..hs.office.migration", |                     "..hs.migration", | ||||||
|                     "..hs.office.partner", |                     "..hs.office.partner", | ||||||
|                     "..hs.office.person", |                     "..hs.office.person", | ||||||
|                     "..hs.office.relation", |                     "..hs.office.relation", | ||||||
| @@ -156,6 +156,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.(*)..", |                     "..hs.office.(*)..", | ||||||
|                     "..hs.booking.(*)..", |                     "..hs.booking.(*)..", | ||||||
|                     "..hs.hosting.(*)..", |                     "..hs.hosting.(*)..", | ||||||
|  |                     "..hs.migration", | ||||||
|                     "..rbac.rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest |                     "..rbac.rbacgrant" // TODO.test: just because of RbacGrantsDiagramServiceIntegrationTest | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
| @@ -167,7 +168,8 @@ public class ArchitectureTest { | |||||||
|             .resideInAnyPackage( |             .resideInAnyPackage( | ||||||
|                     "..hs.booking.(*)..", |                     "..hs.booking.(*)..", | ||||||
|                     "..hs.hosting.(*)..", |                     "..hs.hosting.(*)..", | ||||||
|                     "..hs.validation" // TODO.impl: Some Validators need to be refactored to booking package. |                     "..hs.validation", // TODO.impl: Some Validators need to be refactored to booking package. | ||||||
|  |                     "..hs.migration.." | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
| @@ -177,7 +179,8 @@ public class ArchitectureTest { | |||||||
|             .should().onlyBeAccessed().byClassesThat() |             .should().onlyBeAccessed().byClassesThat() | ||||||
|             .resideInAnyPackage( |             .resideInAnyPackage( | ||||||
|                     "..hs.hosting.(*)..", |                     "..hs.hosting.(*)..", | ||||||
|                     "..hs.booking.(*).." // TODO.impl: fix this cyclic dependency |                     "..hs.booking.(*)..", // TODO.impl: fix this cyclic dependency | ||||||
|  |                     "..hs.migration.." | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
| @@ -189,7 +192,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.bankaccount..", |                     "..hs.office.bankaccount..", | ||||||
|                     "..hs.office.sepamandate..", |                     "..hs.office.sepamandate..", | ||||||
|                     "..hs.office.debitor..", |                     "..hs.office.debitor..", | ||||||
|                     "..hs.office.migration.."); |                     "..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
| @@ -199,7 +202,7 @@ public class ArchitectureTest { | |||||||
|             .resideInAnyPackage( |             .resideInAnyPackage( | ||||||
|                     "..hs.office.sepamandate..", |                     "..hs.office.sepamandate..", | ||||||
|                     "..hs.office.debitor..", |                     "..hs.office.debitor..", | ||||||
|                     "..hs.office.migration.."); |                     "..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
| @@ -212,7 +215,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.partner..", |                     "..hs.office.partner..", | ||||||
|                     "..hs.office.debitor..", |                     "..hs.office.debitor..", | ||||||
|                     "..hs.office.membership..", |                     "..hs.office.membership..", | ||||||
|                     "..hs.office.migration..", |                     "..hs.migration..", | ||||||
|                     "..hs.hosting.asset.." |                     "..hs.hosting.asset.." | ||||||
|                     ); |                     ); | ||||||
|  |  | ||||||
| @@ -227,7 +230,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.partner..", |                     "..hs.office.partner..", | ||||||
|                     "..hs.office.debitor..", |                     "..hs.office.debitor..", | ||||||
|                     "..hs.office.membership..", |                     "..hs.office.membership..", | ||||||
|                     "..hs.office.migration..") |                     "..hs.migration..") | ||||||
|             .orShould().haveNameNotMatching(".*Test$"); |             .orShould().haveNameNotMatching(".*Test$"); | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -239,7 +242,7 @@ public class ArchitectureTest { | |||||||
|             .resideInAnyPackage( |             .resideInAnyPackage( | ||||||
|                     "..hs.office.relation..", |                     "..hs.office.relation..", | ||||||
|                     "..hs.office.partner..", |                     "..hs.office.partner..", | ||||||
|                     "..hs.office.migration..") |                     "..hs.migration..") | ||||||
|             .orShould().haveNameNotMatching(".*Test$"); |             .orShould().haveNameNotMatching(".*Test$"); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
| @@ -251,7 +254,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.partner..", |                     "..hs.office.partner..", | ||||||
|                     "..hs.office.debitor..", |                     "..hs.office.debitor..", | ||||||
|                     "..hs.office.membership..", |                     "..hs.office.membership..", | ||||||
|                     "..hs.office.migration..") |                     "..hs.migration..") | ||||||
|             .orShould().haveNameNotMatching(".*Test$"); |             .orShould().haveNameNotMatching(".*Test$"); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
| @@ -263,7 +266,7 @@ public class ArchitectureTest { | |||||||
|                     "..hs.office.membership..", |                     "..hs.office.membership..", | ||||||
|                     "..hs.office.coopassets..", |                     "..hs.office.coopassets..", | ||||||
|                     "..hs.office.coopshares..", |                     "..hs.office.coopshares..", | ||||||
|                     "..hs.office.migration.."); |                     "..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
| @@ -272,7 +275,7 @@ public class ArchitectureTest { | |||||||
|         .should().onlyBeAccessed().byClassesThat() |         .should().onlyBeAccessed().byClassesThat() | ||||||
|         .resideInAnyPackage( |         .resideInAnyPackage( | ||||||
|                 "..hs.office.coopassets..", |                 "..hs.office.coopassets..", | ||||||
|                 "..hs.office.migration.."); |                 "..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
| @@ -281,14 +284,14 @@ public class ArchitectureTest { | |||||||
|             .should().onlyBeAccessed().byClassesThat() |             .should().onlyBeAccessed().byClassesThat() | ||||||
|             .resideInAnyPackage( |             .resideInAnyPackage( | ||||||
|                     "..hs.office.coopshares..", |                     "..hs.office.coopshares..", | ||||||
|                     "..hs.office.migration.."); |                     "..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
|     public static final ArchRule hsOfficeMigrationPackageRule = classes() |     public static final ArchRule hsOfficeMigrationPackageRule = classes() | ||||||
|             .that().resideInAPackage("..hs.office.migration..") |             .that().resideInAPackage("..hs.migration..") | ||||||
|             .should().onlyBeAccessed().byClassesThat() |             .should().onlyBeAccessed().byClassesThat() | ||||||
|             .resideInAnyPackage("..hs.office.migration.."); |             .resideInAnyPackage("..hs.migration.."); | ||||||
|  |  | ||||||
|     @ArchTest |     @ArchTest | ||||||
|     @SuppressWarnings("unused") |     @SuppressWarnings("unused") | ||||||
|   | |||||||
| @@ -101,7 +101,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|                             "resources": { |                             "resources": { | ||||||
|                                 "RAM": 8, |                                 "RAM": 8, | ||||||
|                                 "SSD": 500, |                                 "SSD": 500, | ||||||
|                                 "CPUs": 2, |                                 "CPU": 2, | ||||||
|                                 "Traffic": 500 |                                 "Traffic": 500 | ||||||
|                             } |                             } | ||||||
|                         }, |                         }, | ||||||
| @@ -114,7 +114,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|                                 "HDD": 10000, |                                 "HDD": 10000, | ||||||
|                                 "RAM": 32, |                                 "RAM": 32, | ||||||
|                                 "SSD": 4000, |                                 "SSD": 4000, | ||||||
|                                 "CPUs": 10, |                                 "CPU": 10, | ||||||
|                                 "Traffic": 2000 |                                 "Traffic": 2000 | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
| @@ -148,7 +148,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|                                 "type": "MANAGED_SERVER", |                                 "type": "MANAGED_SERVER", | ||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validTo": "{validTo}", |                                 "validTo": "{validTo}", | ||||||
|                                 "resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } | ||||||
|                             } |                             } | ||||||
|                             """ |                             """ | ||||||
|                                 .replace("{projectUuid}", givenProject.getUuid().toString()) |                                 .replace("{projectUuid}", givenProject.getUuid().toString()) | ||||||
| @@ -166,7 +166,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validFrom": "{today}", |                                 "validFrom": "{today}", | ||||||
|                                 "validTo": "{todayPlus1Month}", |                                 "validTo": "{todayPlus1Month}", | ||||||
|                                 "resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "SSD": 100, "Traffic": 250 } | ||||||
|                              } |                              } | ||||||
|                             """ |                             """ | ||||||
|                                 .replace("{today}", LocalDate.now().toString()) |                                 .replace("{today}", LocalDate.now().toString()) | ||||||
| @@ -267,7 +267,7 @@ class HsBookingItemControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|                             "resources": { |                             "resources": { | ||||||
|                                 "RAM": 8, |                                 "RAM": 8, | ||||||
|                                 "SSD": 500, |                                 "SSD": 500, | ||||||
|                                 "CPUs": 2, |                                 "CPU": 2, | ||||||
|                                 "Traffic": 500 |                                 "Traffic": 500 | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ class HsBookingItemControllerRestTest { | |||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validTo": "{validTo}", |                                 "validTo": "{validTo}", | ||||||
|                                 "garbage": "should not be accepted", |                                 "garbage": "should not be accepted", | ||||||
|                                 "resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } | ||||||
|                             } |                             } | ||||||
|                             """ |                             """ | ||||||
|                                     .replace("{projectUuid}", givenProjectUuid.toString()) |                                     .replace("{projectUuid}", givenProjectUuid.toString()) | ||||||
| @@ -108,7 +108,7 @@ class HsBookingItemControllerRestTest { | |||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validFrom": "{today}", |                                 "validFrom": "{today}", | ||||||
|                                 "validTo": "{todayPlus1Month}", |                                 "validTo": "{todayPlus1Month}", | ||||||
|                                 "resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "SSD": 100, "Traffic": 250 } | ||||||
|                              } |                              } | ||||||
|                             """ |                             """ | ||||||
|                                     .replace("{today}", LocalDate.now().toString()) |                                     .replace("{today}", LocalDate.now().toString()) | ||||||
| @@ -141,7 +141,7 @@ class HsBookingItemControllerRestTest { | |||||||
|                                 "type": "MANAGED_SERVER", |                                 "type": "MANAGED_SERVER", | ||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validFrom": "{validFrom}", |                                 "validFrom": "{validFrom}", | ||||||
|                                 "resources": { "CPUs": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "RAM": 4, "SSD": 100, "Traffic": 250 } | ||||||
|                             } |                             } | ||||||
|                             """ |                             """ | ||||||
|                                     .replace("{projectUuid}", givenProjectUuid.toString()) |                                     .replace("{projectUuid}", givenProjectUuid.toString()) | ||||||
| @@ -159,7 +159,7 @@ class HsBookingItemControllerRestTest { | |||||||
|                                 "caption": "some new booking", |                                 "caption": "some new booking", | ||||||
|                                 "validFrom": "{today}", |                                 "validFrom": "{today}", | ||||||
|                                 "validTo": null, |                                 "validTo": null, | ||||||
|                                 "resources": { "CPUs": 12, "SSD": 100, "Traffic": 250 } |                                 "resources": { "CPU": 12, "SSD": 100, "Traffic": 250 } | ||||||
|                              } |                              } | ||||||
|                             """ |                             """ | ||||||
|                             .replace("{today}", LocalDate.now().toString()) |                             .replace("{today}", LocalDate.now().toString()) | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ class HsBookingItemEntityUnitTest { | |||||||
|             .type(HsBookingItemType.CLOUD_SERVER) |             .type(HsBookingItemType.CLOUD_SERVER) | ||||||
|             .caption("some caption") |             .caption("some caption") | ||||||
|             .resources(Map.ofEntries( |             .resources(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("SSD-storage", 512), |                     entry("SSD-storage", 512), | ||||||
|                     entry("HDD-storage", 2048))) |                     entry("HDD-storage", 2048))) | ||||||
|             .validity(toPostgresDateRange(GIVEN_VALID_FROM, GIVEN_VALID_TO)) |             .validity(toPostgresDateRange(GIVEN_VALID_FROM, GIVEN_VALID_TO)) | ||||||
| @@ -53,7 +53,7 @@ class HsBookingItemEntityUnitTest { | |||||||
|     void toStringContainsAllPropertiesAndResourcesSortedByKey() { |     void toStringContainsAllPropertiesAndResourcesSortedByKey() { | ||||||
|         final var result = givenBookingItem.toString(); |         final var result = givenBookingItem.toString(); | ||||||
|  |  | ||||||
|         assertThat(result).isEqualToIgnoringWhitespace("HsBookingItemEntity(D-1234500:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { \"CPUs\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })"); |         assertThat(result).isEqualToIgnoringWhitespace("HsBookingItemEntity(D-1234500:test project, CLOUD_SERVER, [2020-01-01,2031-01-01), some caption, { \"CPU\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|   | |||||||
| @@ -171,8 +171,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup | |||||||
|             allTheseBookingItemsAreReturned( |             allTheseBookingItemsAreReturned( | ||||||
|                     result, |                     result, | ||||||
|                     "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )", |                     "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )", | ||||||
|                     "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SSD: 500, Traffic: 500 } )", |                     "HsBookingItemEntity(D-1000212:D-1000212 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPU: 2, RAM: 8, SSD: 500, Traffic: 500 } )", | ||||||
|                     "HsBookingItemEntity(D-1000212:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )"); |                     "HsBookingItemEntity(D-1000212:D-1000212 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )"); | ||||||
|              assertThat(result.stream().filter(bi -> bi.getRelatedHostingAsset()!=null).findAny()) |              assertThat(result.stream().filter(bi -> bi.getRelatedHostingAsset()!=null).findAny()) | ||||||
|                      .as("at least one relatedProject expected, but none found => fetching relatedProject does not work") |                      .as("at least one relatedProject expected, but none found => fetching relatedProject does not work") | ||||||
|                      .isNotEmpty(); |                      .isNotEmpty(); | ||||||
| @@ -194,8 +194,8 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup | |||||||
|             exactlyTheseBookingItemsAreReturned( |             exactlyTheseBookingItemsAreReturned( | ||||||
|                     result, |                     result, | ||||||
|                     "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )", |                     "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_WEBSPACE, [2022-10-01,), separate ManagedWebspace, { Daemons: 0, Multi: 1, SSD: 100, Traffic: 50 } )", | ||||||
|                     "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPUs: 2, RAM: 8, SSD: 500, Traffic: 500 } )", |                     "HsBookingItemEntity(D-1000111:D-1000111 default project, MANAGED_SERVER, [2022-10-01,), separate ManagedServer, { CPU: 2, RAM: 8, SSD: 500, Traffic: 500 } )", | ||||||
|                     "HsBookingItemEntity(D-1000111:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPUs: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )"); |                     "HsBookingItemEntity(D-1000111:D-1000111 default project, PRIVATE_CLOUD, [2024-04-01,), some PrivateCloud, { CPU: 10, HDD: 10000, RAM: 32, SSD: 4000, Traffic: 2000 } )"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -211,7 +211,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup | |||||||
|             final var result = jpaAttempt.transacted(() -> { |             final var result = jpaAttempt.transacted(() -> { | ||||||
|                 context("superuser-alex@hostsharing.net"); |                 context("superuser-alex@hostsharing.net"); | ||||||
|                 final var foundBookingItem = em.find(HsBookingItemEntity.class, givenBookingItemUuid); |                 final var foundBookingItem = em.find(HsBookingItemEntity.class, givenBookingItemUuid); | ||||||
|                 foundBookingItem.getResources().put("CPUs", 2); |                 foundBookingItem.getResources().put("CPU", 2); | ||||||
|                 foundBookingItem.getResources().remove("SSD-storage"); |                 foundBookingItem.getResources().remove("SSD-storage"); | ||||||
|                 foundBookingItem.getResources().put("HSD-storage", 2048); |                 foundBookingItem.getResources().put("HSD-storage", 2048); | ||||||
|                 foundBookingItem.setValidity(Range.closedOpen( |                 foundBookingItem.setValidity(Range.closedOpen( | ||||||
| @@ -336,7 +336,7 @@ class HsBookingItemRepositoryIntegrationTest extends ContextBasedTestWithCleanup | |||||||
|                     .validity(Range.closedOpen( |                     .validity(Range.closedOpen( | ||||||
|                             LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01"))) |                             LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01"))) | ||||||
|                     .resources(Map.ofEntries( |                     .resources(Map.ofEntries( | ||||||
|                             entry("CPUs", 1), |                             entry("CPU", 1), | ||||||
|                             entry("SSD-storage", 256))) |                             entry("SSD-storage", 256))) | ||||||
|                     .build(); |                     .build(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ public class TestHsBookingItem { | |||||||
|             .type(HsBookingItemType.CLOUD_SERVER) |             .type(HsBookingItemType.CLOUD_SERVER) | ||||||
|             .caption("test cloud server booking item") |             .caption("test cloud server booking item") | ||||||
|             .resources(Map.ofEntries( |             .resources(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("RAM", 4), |                     entry("RAM", 4), | ||||||
|                     entry("SSD", 50), |                     entry("SSD", 50), | ||||||
|                     entry("Traffic", 250) |                     entry("Traffic", 250) | ||||||
| @@ -30,7 +30,7 @@ public class TestHsBookingItem { | |||||||
|             .type(HsBookingItemType.MANAGED_SERVER) |             .type(HsBookingItemType.MANAGED_SERVER) | ||||||
|             .caption("test project booking item") |             .caption("test project booking item") | ||||||
|             .resources(Map.ofEntries( |             .resources(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("RAM", 4), |                     entry("RAM", 4), | ||||||
|                     entry("SSD", 50), |                     entry("SSD", 50), | ||||||
|                     entry("Traffic", 250) |                     entry("Traffic", 250) | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ class HsBookingItemEntityValidatorUnitTest { | |||||||
|         // then |         // then | ||||||
|         assertThat(result).isInstanceOf(ValidationException.class) |         assertThat(result).isInstanceOf(ValidationException.class) | ||||||
|                 .hasMessageContaining( |                 .hasMessageContaining( | ||||||
|                         "'D-12345:test project:Test-Server.resources.CPUs' is required but missing", |                         "'D-12345:test project:Test-Server.resources.CPU' is required but missing", | ||||||
|                         "'D-12345:test project:Test-Server.resources.RAM' is required but missing", |                         "'D-12345:test project:Test-Server.resources.RAM' is required but missing", | ||||||
|                         "'D-12345:test project:Test-Server.resources.SSD' is required but missing", |                         "'D-12345:test project:Test-Server.resources.SSD' is required but missing", | ||||||
|                         "'D-12345:test project:Test-Server.resources.Traffic' is required but missing"); |                         "'D-12345:test project:Test-Server.resources.Traffic' is required but missing"); | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|                 .project(project) |                 .project(project) | ||||||
|                 .caption("Test-Server") |                 .caption("Test-Server") | ||||||
|                 .resources(Map.ofEntries( |                 .resources(Map.ofEntries( | ||||||
|                         entry("CPUs", 2), |                         entry("CPU", 2), | ||||||
|                         entry("RAM", 25), |                         entry("RAM", 25), | ||||||
|                         entry("SSD", 25), |                         entry("SSD", 25), | ||||||
|                         entry("Traffic", 250), |                         entry("Traffic", 250), | ||||||
| @@ -56,11 +56,12 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|         // then |         // then | ||||||
|         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( |         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( | ||||||
|                 "{type=boolean, propertyName=active, defaultValue=true}", |                 "{type=boolean, propertyName=active, defaultValue=true}", | ||||||
|                 "{type=integer, propertyName=CPUs, min=1, max=32, required=true}", |                 "{type=integer, propertyName=CPU, min=1, max=32, required=true}", | ||||||
|                 "{type=integer, propertyName=RAM, unit=GB, min=1, max=128, required=true}", |                 "{type=integer, propertyName=RAM, unit=GB, min=1, max=8192, required=true}", | ||||||
|                 "{type=integer, propertyName=SSD, unit=GB, min=0, max=1000, step=25, required=true}", |                 "{type=integer, propertyName=SSD, unit=GB, min=25, max=1000, step=25, requiresAtLeastOneOf=[SDD, HDD]}", | ||||||
|                 "{type=integer, propertyName=HDD, unit=GB, min=0, max=4000, step=250, defaultValue=0}", |                 "{type=integer, propertyName=HDD, unit=GB, min=250, max=4000, step=250, requiresAtLeastOneOf=[SSD, HDD]}", | ||||||
|                 "{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, required=true}", |                 "{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic]}", | ||||||
|  |                 "{type=integer, propertyName=Bandwidth, unit=GB, min=250, max=10000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic]}", | ||||||
|                 "{type=enumeration, propertyName=SLA-Infrastructure, values=[BASIC, EXT8H, EXT4H, EXT2H]}"); |                 "{type=enumeration, propertyName=SLA-Infrastructure, values=[BASIC, EXT8H, EXT4H, EXT2H]}"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -71,7 +72,7 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|                 .type(CLOUD_SERVER) |                 .type(CLOUD_SERVER) | ||||||
|                 .caption("Test Cloud-Server") |                 .caption("Test Cloud-Server") | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 2), |                         entry("CPU", 2), | ||||||
|                         entry("RAM", 10), |                         entry("RAM", 10), | ||||||
|                         entry("SSD", 50), |                         entry("SSD", 50), | ||||||
|                         entry("Traffic", 2500) |                         entry("Traffic", 2500) | ||||||
| @@ -81,7 +82,7 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|                 .type(MANAGED_SERVER) |                 .type(MANAGED_SERVER) | ||||||
|                 .caption("Test Managed-Server") |                 .caption("Test Managed-Server") | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 3), |                         entry("CPU", 3), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 3000) |                         entry("Traffic", 3000) | ||||||
| @@ -92,7 +93,7 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|                 .project(project) |                 .project(project) | ||||||
|                 .caption("Test Cloud") |                 .caption("Test Cloud") | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 4), |                         entry("CPU", 4), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 5000) |                         entry("Traffic", 5000) | ||||||
| @@ -110,7 +111,7 @@ class HsCloudServerBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(result).containsExactlyInAnyOrder( |         assertThat(result).containsExactlyInAnyOrder( | ||||||
|                 "'D-12345:Test-Project:Test Cloud.resources.CPUs' maximum total is 4, but actual total CPUs is 5", |                 "'D-12345:Test-Project:Test Cloud.resources.CPU' maximum total is 4, but actual total CPU is 5", | ||||||
|                 "'D-12345:Test-Project:Test Cloud.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", |                 "'D-12345:Test-Project:Test Cloud.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", | ||||||
|                 "'D-12345:Test-Project:Test Cloud.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", |                 "'D-12345:Test-Project:Test Cloud.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", | ||||||
|                 "'D-12345:Test-Project:Test Cloud.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB" |                 "'D-12345:Test-Project:Test Cloud.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB" | ||||||
|   | |||||||
| @@ -40,7 +40,7 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|                 .type(MANAGED_SERVER) |                 .type(MANAGED_SERVER) | ||||||
|                 .project(project) |                 .project(project) | ||||||
|                 .resources(Map.ofEntries( |                 .resources(Map.ofEntries( | ||||||
|                         entry("CPUs", 2), |                         entry("CPU", 2), | ||||||
|                         entry("RAM", 25), |                         entry("RAM", 25), | ||||||
|                         entry("SSD", 25), |                         entry("SSD", 25), | ||||||
|                         entry("Traffic", 250), |                         entry("Traffic", 250), | ||||||
| @@ -63,11 +63,12 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( |         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( | ||||||
|                 "{type=integer, propertyName=CPUs, min=1, max=32, required=true}", |                 "{type=integer, propertyName=CPU, min=1, max=32, required=true}", | ||||||
|                 "{type=integer, propertyName=RAM, unit=GB, min=1, max=128, required=true}", |                 "{type=integer, propertyName=RAM, unit=GB, min=1, max=128, required=true}", | ||||||
|                 "{type=integer, propertyName=SSD, unit=GB, min=25, max=1000, step=25, required=true, isTotalsValidator=true, thresholdPercentage=200}", |                 "{type=integer, propertyName=SSD, unit=GB, min=25, max=2000, step=25, requiresAtLeastOneOf=[SSD, HDD], isTotalsValidator=true, thresholdPercentage=200}", | ||||||
|                 "{type=integer, propertyName=HDD, unit=GB, min=0, max=4000, step=250, defaultValue=0, isTotalsValidator=true, thresholdPercentage=200}", |                 "{type=integer, propertyName=HDD, unit=GB, min=250, max=10000, step=250, requiresAtLeastOneOf=[SSD, HDD], isTotalsValidator=true, thresholdPercentage=200}", | ||||||
|                 "{type=integer, propertyName=Traffic, unit=GB, min=250, max=10000, step=250, required=true, isTotalsValidator=true, thresholdPercentage=200}", |                 "{type=integer, propertyName=Traffic, unit=GB, min=250, max=64000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic], isTotalsValidator=true, thresholdPercentage=200}", | ||||||
|  |                 "{type=integer, propertyName=Bandwidth, unit=GB, min=250, max=64000, step=250, requiresAtMaxOneOf=[Bandwidth, Traffic], isTotalsValidator=true, thresholdPercentage=200}", | ||||||
|                 "{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT8H, EXT4H, EXT2H], defaultValue=BASIC}", |                 "{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT8H, EXT4H, EXT2H], defaultValue=BASIC}", | ||||||
|                 "{type=boolean, propertyName=SLA-EMail}", // TODO.impl: falseIf-validation is missing in output |                 "{type=boolean, propertyName=SLA-EMail}", // TODO.impl: falseIf-validation is missing in output | ||||||
|                 "{type=boolean, propertyName=SLA-Maria}", |                 "{type=boolean, propertyName=SLA-Maria}", | ||||||
| @@ -82,7 +83,7 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|         final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder() |         final var subCloudServerBookingItemEntity = HsBookingItemEntity.builder() | ||||||
|                 .type(CLOUD_SERVER) |                 .type(CLOUD_SERVER) | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 2), |                         entry("CPU", 2), | ||||||
|                         entry("RAM", 10), |                         entry("RAM", 10), | ||||||
|                         entry("SSD", 50), |                         entry("SSD", 50), | ||||||
|                         entry("Traffic", 2500) |                         entry("Traffic", 2500) | ||||||
| @@ -91,7 +92,7 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|         final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder() |         final HsBookingItemEntity subManagedServerBookingItemEntity = HsBookingItemEntity.builder() | ||||||
|                 .type(MANAGED_SERVER) |                 .type(MANAGED_SERVER) | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 3), |                         entry("CPU", 3), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 3000) |                         entry("Traffic", 3000) | ||||||
| @@ -101,7 +102,7 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|                 .type(PRIVATE_CLOUD) |                 .type(PRIVATE_CLOUD) | ||||||
|                 .project(project) |                 .project(project) | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 4), |                         entry("CPU", 4), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 5000) |                         entry("Traffic", 5000) | ||||||
| @@ -120,7 +121,7 @@ class HsManagedServerBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(result).containsExactlyInAnyOrder( |         assertThat(result).containsExactlyInAnyOrder( | ||||||
|                 "'D-12345:Test-Project:null.resources.CPUs' maximum total is 4, but actual total CPUs is 5", |                 "'D-12345:Test-Project:null.resources.CPU' maximum total is 4, but actual total CPU is 5", | ||||||
|                 "'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", |                 "'D-12345:Test-Project:null.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", | ||||||
|                 "'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", |                 "'D-12345:Test-Project:null.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", | ||||||
|                 "'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB" |                 "'D-12345:Test-Project:null.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB" | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { | |||||||
|                 .project(project) |                 .project(project) | ||||||
|                 .caption("Test Managed-Webspace") |                 .caption("Test Managed-Webspace") | ||||||
|                 .resources(Map.ofEntries( |                 .resources(Map.ofEntries( | ||||||
|                         entry("CPUs", 2), |                         entry("CPU", 2), | ||||||
|                         entry("RAM", 25), |                         entry("RAM", 25), | ||||||
|                         entry("Traffic", 250), |                         entry("Traffic", 250), | ||||||
|                         entry("SLA-EMail", true) |                         entry("SLA-EMail", true) | ||||||
| @@ -41,7 +41,7 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(result).containsExactlyInAnyOrder( |         assertThat(result).containsExactlyInAnyOrder( | ||||||
|                 "'D-12345:Test-Project:Test Managed-Webspace.resources.CPUs' is not expected but is set to '2'", |                 "'D-12345:Test-Project:Test Managed-Webspace.resources.CPU' is not expected but is set to '2'", | ||||||
|                 "'D-12345:Test-Project:Test Managed-Webspace.resources.RAM' is not expected but is set to '25'", |                 "'D-12345:Test-Project:Test Managed-Webspace.resources.RAM' is not expected but is set to '25'", | ||||||
|                 "'D-12345:Test-Project:Test Managed-Webspace.resources.SSD' is required but missing", |                 "'D-12345:Test-Project:Test Managed-Webspace.resources.SSD' is required but missing", | ||||||
|                 "'D-12345:Test-Project:Test Managed-Webspace.resources.SLA-EMail' is not expected but is set to 'true'" |                 "'D-12345:Test-Project:Test Managed-Webspace.resources.SLA-EMail' is not expected but is set to 'true'" | ||||||
| @@ -55,11 +55,12 @@ class HsManagedWebspaceBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( |         assertThat(validator.properties()).map(Map::toString).containsExactlyInAnyOrder( | ||||||
|                 "{type=integer, propertyName=SSD, unit=GB, min=1, max=100, step=1, required=true}", |                 "{type=integer, propertyName=SSD, unit=GB, min=1, max=2000, step=1, required=true}", | ||||||
|                 "{type=integer, propertyName=HDD, unit=GB, min=0, max=250, step=10}", |                 "{type=integer, propertyName=HDD, unit=GB, min=0, max=10000, step=10}", | ||||||
|                 "{type=integer, propertyName=Traffic, unit=GB, min=10, max=1000, step=10, required=true}", |                 "{type=integer, propertyName=Traffic, unit=GB, min=10, max=64000, step=10, requiresAtMaxOneOf=[Bandwidth, Traffic]}", | ||||||
|  |                 "{type=integer, propertyName=Bandwidth, unit=GB, min=10, max=1000, step=10, requiresAtMaxOneOf=[Bandwidth, Traffic]}", | ||||||
|                 "{type=integer, propertyName=Multi, min=1, max=100, step=1, defaultValue=1}", |                 "{type=integer, propertyName=Multi, min=1, max=100, step=1, defaultValue=1}", | ||||||
|                 "{type=integer, propertyName=Daemons, min=0, max=10, defaultValue=0}", |                 "{type=integer, propertyName=Daemons, min=0, max=16, defaultValue=0}", | ||||||
|                 "{type=boolean, propertyName=Online Office Server}", |                 "{type=boolean, propertyName=Online Office Server}", | ||||||
|                 "{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT24H], defaultValue=BASIC}"); |                 "{type=enumeration, propertyName=SLA-Platform, values=[BASIC, EXT24H], defaultValue=BASIC}"); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import static java.util.Map.ofEntries; | |||||||
| import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; | import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.CLOUD_SERVER; | ||||||
| import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; | import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.MANAGED_SERVER; | ||||||
| import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; | import static net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType.PRIVATE_CLOUD; | ||||||
|  | import static net.hostsharing.hsadminng.hs.booking.project.TestHsBookingProject.TEST_PROJECT; | ||||||
| import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||||
|  |  | ||||||
| class HsPrivateCloudBookingItemValidatorUnitTest { | class HsPrivateCloudBookingItemValidatorUnitTest { | ||||||
| @@ -28,9 +29,10 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|         // given |         // given | ||||||
|         final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() |         final var privateCloudBookingItemEntity = HsBookingItemEntity.builder() | ||||||
|                 .type(PRIVATE_CLOUD) |                 .type(PRIVATE_CLOUD) | ||||||
|  |                 .project(TEST_PROJECT) | ||||||
|                 .caption("myPC") |                 .caption("myPC") | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 4), |                         entry("CPU", 4), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 5000), |                         entry("Traffic", 5000), | ||||||
| @@ -42,7 +44,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|                                 .type(MANAGED_SERVER) |                                 .type(MANAGED_SERVER) | ||||||
|                                 .caption("myMS-1") |                                 .caption("myMS-1") | ||||||
|                                 .resources(ofEntries( |                                 .resources(ofEntries( | ||||||
|                                         entry("CPUs", 2), |                                         entry("CPU", 2), | ||||||
|                                         entry("RAM", 10), |                                         entry("RAM", 10), | ||||||
|                                         entry("SSD", 50), |                                         entry("SSD", 50), | ||||||
|                                         entry("Traffic", 2500), |                                         entry("Traffic", 2500), | ||||||
| @@ -54,7 +56,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|                                 .type(CLOUD_SERVER) |                                 .type(CLOUD_SERVER) | ||||||
|                                 .caption("myMS-2") |                                 .caption("myMS-2") | ||||||
|                                 .resources(ofEntries( |                                 .resources(ofEntries( | ||||||
|                                         entry("CPUs", 2), |                                         entry("CPU", 2), | ||||||
|                                         entry("RAM", 10), |                                         entry("RAM", 10), | ||||||
|                                         entry("SSD", 50), |                                         entry("SSD", 50), | ||||||
|                                         entry("Traffic", 2500), |                                         entry("Traffic", 2500), | ||||||
| @@ -80,7 +82,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|                 .type(PRIVATE_CLOUD) |                 .type(PRIVATE_CLOUD) | ||||||
|                 .caption("myPC") |                 .caption("myPC") | ||||||
|                 .resources(ofEntries( |                 .resources(ofEntries( | ||||||
|                         entry("CPUs", 4), |                         entry("CPU", 4), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 100), |                         entry("SSD", 100), | ||||||
|                         entry("Traffic", 5000), |                         entry("Traffic", 5000), | ||||||
| @@ -92,7 +94,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|                                 .type(MANAGED_SERVER) |                                 .type(MANAGED_SERVER) | ||||||
|                                 .caption("myMS-1") |                                 .caption("myMS-1") | ||||||
|                                 .resources(ofEntries( |                                 .resources(ofEntries( | ||||||
|                                         entry("CPUs", 3), |                                         entry("CPU", 3), | ||||||
|                                         entry("RAM", 20), |                                         entry("RAM", 20), | ||||||
|                                         entry("SSD", 100), |                                         entry("SSD", 100), | ||||||
|                                         entry("Traffic", 3000), |                                         entry("Traffic", 3000), | ||||||
| @@ -104,7 +106,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|                                 .type(CLOUD_SERVER) |                                 .type(CLOUD_SERVER) | ||||||
|                                 .caption("myMS-2") |                                 .caption("myMS-2") | ||||||
|                                 .resources(ofEntries( |                                 .resources(ofEntries( | ||||||
|                                         entry("CPUs", 2), |                                         entry("CPU", 2), | ||||||
|                                         entry("RAM", 10), |                                         entry("RAM", 10), | ||||||
|                                         entry("SSD", 50), |                                         entry("SSD", 50), | ||||||
|                                         entry("Traffic", 2500), |                                         entry("Traffic", 2500), | ||||||
| @@ -124,7 +126,7 @@ class HsPrivateCloudBookingItemValidatorUnitTest { | |||||||
|  |  | ||||||
|         // then |         // then | ||||||
|         assertThat(result).containsExactlyInAnyOrder( |         assertThat(result).containsExactlyInAnyOrder( | ||||||
|                 "'D-12345:Test-Project:myPC.resources.CPUs' maximum total is 4, but actual total CPUs is 5", |                 "'D-12345:Test-Project:myPC.resources.CPU' maximum total is 4, but actual total CPU is 5", | ||||||
|                 "'D-12345:Test-Project:myPC.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", |                 "'D-12345:Test-Project:myPC.resources.RAM' maximum total is 20 GB, but actual total RAM is 30 GB", | ||||||
|                 "'D-12345:Test-Project:myPC.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", |                 "'D-12345:Test-Project:myPC.resources.SSD' maximum total is 100 GB, but actual total SSD is 150 GB", | ||||||
|                 "'D-12345:Test-Project:myPC.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB", |                 "'D-12345:Test-Project:myPC.resources.Traffic' maximum total is 5000 GB, but actual total Traffic is 5500 GB", | ||||||
|   | |||||||
| @@ -702,7 +702,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup | |||||||
|             context.define("superuser-alex@hostsharing.net"); |             context.define("superuser-alex@hostsharing.net"); | ||||||
|             final var project = projectRepo.findByCaption(projectCaption).getFirst(); |             final var project = projectRepo.findByCaption(projectCaption).getFirst(); | ||||||
|             final var resources = switch (bookingItemType) { |             final var resources = switch (bookingItemType) { | ||||||
|                 case MANAGED_SERVER -> Map.<String, Object>ofEntries(entry("CPUs", 1), |                 case MANAGED_SERVER -> Map.<String, Object>ofEntries(entry("CPU", 1), | ||||||
|                         entry("RAM", 20), |                         entry("RAM", 20), | ||||||
|                         entry("SSD", 25), |                         entry("SSD", 25), | ||||||
|                         entry("Traffic", 250)); |                         entry("Traffic", 250)); | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ class HsHostingAssetEntityUnitTest { | |||||||
|             .identifier("vm1234") |             .identifier("vm1234") | ||||||
|             .caption("some managed asset") |             .caption("some managed asset") | ||||||
|             .config(Map.ofEntries( |             .config(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("SSD-storage", 512), |                     entry("SSD-storage", 512), | ||||||
|                     entry("HDD-storage", 2048))) |                     entry("HDD-storage", 2048))) | ||||||
|             .build(); |             .build(); | ||||||
| @@ -27,7 +27,7 @@ class HsHostingAssetEntityUnitTest { | |||||||
|             .identifier("xyz00") |             .identifier("xyz00") | ||||||
|             .caption("some managed webspace") |             .caption("some managed webspace") | ||||||
|             .config(Map.ofEntries( |             .config(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("SSD-storage", 512), |                     entry("SSD-storage", 512), | ||||||
|                     entry("HDD-storage", 2048))) |                     entry("HDD-storage", 2048))) | ||||||
|             .build(); |             .build(); | ||||||
| @@ -58,7 +58,7 @@ class HsHostingAssetEntityUnitTest { | |||||||
|     void toStringContainsAllPropertiesAndResourcesSortedByKey() { |     void toStringContainsAllPropertiesAndResourcesSortedByKey() { | ||||||
|  |  | ||||||
|         assertThat(givenWebspace.toString()).isEqualToIgnoringWhitespace( |         assertThat(givenWebspace.toString()).isEqualToIgnoringWhitespace( | ||||||
|                 "HsHostingAssetEntity(MANAGED_WEBSPACE, xyz00, some managed webspace, MANAGED_SERVER:vm1234, D-1234500:test project:test cloud server booking item, { \"CPUs\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })"); |                 "HsHostingAssetEntity(MANAGED_WEBSPACE, xyz00, some managed webspace, MANAGED_SERVER:vm1234, D-1234500:test project:test cloud server booking item, { \"CPU\": 2, \"HDD-storage\": 2048, \"SSD-storage\": 512 })"); | ||||||
|  |  | ||||||
|         assertThat(givenUnixUser.toString()).isEqualToIgnoringWhitespace( |         assertThat(givenUnixUser.toString()).isEqualToIgnoringWhitespace( | ||||||
|                 "HsHostingAssetEntity(UNIX_USER, xyz00-web, some unix-user, MANAGED_WEBSPACE:xyz00, { \"HDD-hard-quota\": 512, \"HDD-soft-quota\": 256, \"SSD-hard-quota\": 256, \"SSD-soft-quota\": 128 })"); |                 "HsHostingAssetEntity(UNIX_USER, xyz00-web, some unix-user, MANAGED_WEBSPACE:xyz00, { \"HDD-hard-quota\": 512, \"HDD-soft-quota\": 256, \"SSD-hard-quota\": 256, \"SSD-soft-quota\": 128 })"); | ||||||
|   | |||||||
| @@ -263,7 +263,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu | |||||||
|             final var result = jpaAttempt.transacted(() -> { |             final var result = jpaAttempt.transacted(() -> { | ||||||
|                 context("superuser-alex@hostsharing.net"); |                 context("superuser-alex@hostsharing.net"); | ||||||
|                 final var foundAsset = em.find(HsHostingAssetEntity.class, givenAssetUuid); |                 final var foundAsset = em.find(HsHostingAssetEntity.class, givenAssetUuid); | ||||||
|                 foundAsset.getConfig().put("CPUs", 2); |                 foundAsset.getConfig().put("CPU", 2); | ||||||
|                 foundAsset.getConfig().remove("SSD-storage"); |                 foundAsset.getConfig().remove("SSD-storage"); | ||||||
|                 foundAsset.getConfig().put("HSD-storage", 2048); |                 foundAsset.getConfig().put("HSD-storage", 2048); | ||||||
|                 return toCleanup(assetRepo.save(foundAsset)); |                 return toCleanup(assetRepo.save(foundAsset)); | ||||||
| @@ -404,7 +404,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu | |||||||
|                     .identifier(identifier) |                     .identifier(identifier) | ||||||
|                     .caption("some temp cloud asset") |                     .caption("some temp cloud asset") | ||||||
|                     .config(Map.ofEntries( |                     .config(Map.ofEntries( | ||||||
|                             entry("CPUs", 1), |                             entry("CPU", 1), | ||||||
|                             entry("SSD-storage", 256))) |                             entry("SSD-storage", 256))) | ||||||
|                     .build(); |                     .build(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ class HsHostingAssetTypeUnitTest { | |||||||
|                 ## HostingAsset Type Structure |                 ## HostingAsset Type Structure | ||||||
|                  |                  | ||||||
|                  |                  | ||||||
|                 ### Webspace+Server |                 ### Server+Webspace | ||||||
|                  |                  | ||||||
|                 ```plantuml |                 ```plantuml | ||||||
|                 @startuml |                 @startuml | ||||||
| @@ -35,6 +35,12 @@ class HsHostingAssetTypeUnitTest { | |||||||
|                         entity HA_IPV6_NUMBER |                         entity HA_IPV6_NUMBER | ||||||
|                     } |                     } | ||||||
|                  |                  | ||||||
|  |                     package Webspace #99bcdb { | ||||||
|  |                         entity HA_MANAGED_WEBSPACE | ||||||
|  |                         entity HA_UNIX_USER | ||||||
|  |                         entity HA_EMAIL_ALIAS | ||||||
|  |                     } | ||||||
|  |                  | ||||||
|                 } |                 } | ||||||
|                  |                  | ||||||
|                 BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD |                 BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD | ||||||
| @@ -43,10 +49,16 @@ class HsHostingAssetTypeUnitTest { | |||||||
|                  |                  | ||||||
|                 HA_CLOUD_SERVER *==> BI_CLOUD_SERVER |                 HA_CLOUD_SERVER *==> BI_CLOUD_SERVER | ||||||
|                 HA_MANAGED_SERVER *==> BI_MANAGED_SERVER |                 HA_MANAGED_SERVER *==> BI_MANAGED_SERVER | ||||||
|  |                 HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE | ||||||
|  |                 HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER | ||||||
|  |                 HA_UNIX_USER *==> HA_MANAGED_WEBSPACE | ||||||
|  |                 HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE | ||||||
|                 HA_IPV4_NUMBER o..> HA_CLOUD_SERVER |                 HA_IPV4_NUMBER o..> HA_CLOUD_SERVER | ||||||
|                 HA_IPV4_NUMBER o..> HA_MANAGED_SERVER |                 HA_IPV4_NUMBER o..> HA_MANAGED_SERVER | ||||||
|  |                 HA_IPV4_NUMBER o..> HA_MANAGED_WEBSPACE | ||||||
|                 HA_IPV6_NUMBER o..> HA_CLOUD_SERVER |                 HA_IPV6_NUMBER o..> HA_CLOUD_SERVER | ||||||
|                 HA_IPV6_NUMBER o..> HA_MANAGED_SERVER |                 HA_IPV6_NUMBER o..> HA_MANAGED_SERVER | ||||||
|  |                 HA_IPV6_NUMBER o..> HA_MANAGED_WEBSPACE | ||||||
|                  |                  | ||||||
|                 package Legend #white { |                 package Legend #white { | ||||||
|                     SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY |                     SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { | |||||||
|             .type(HsBookingItemType.MANAGED_SERVER) |             .type(HsBookingItemType.MANAGED_SERVER) | ||||||
|             .caption("Test Managed-Server") |             .caption("Test Managed-Server") | ||||||
|             .resources(Map.ofEntries( |             .resources(Map.ofEntries( | ||||||
|                     entry("CPUs", 2), |                     entry("CPU", 2), | ||||||
|                     entry("RAM", 25), |                     entry("RAM", 25), | ||||||
|                     entry("SSD", 25), |                     entry("SSD", 25), | ||||||
|                     entry("Traffic", 250), |                     entry("Traffic", 250), | ||||||
| @@ -125,6 +125,7 @@ class HsManagedWebspaceHostingAssetValidatorUnitTest { | |||||||
|                 .type(MANAGED_WEBSPACE) |                 .type(MANAGED_WEBSPACE) | ||||||
|                 .bookingItem(HsBookingItemEntity.builder() |                 .bookingItem(HsBookingItemEntity.builder() | ||||||
|                         .type(HsBookingItemType.MANAGED_WEBSPACE) |                         .type(HsBookingItemType.MANAGED_WEBSPACE) | ||||||
|  |                         .project(TEST_PROJECT) | ||||||
|                         .caption("some ManagedWebspace") |                         .caption("some ManagedWebspace") | ||||||
|                         .resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250))) |                         .resources(Map.ofEntries(entry("SSD", 25), entry("Traffic", 250))) | ||||||
|                         .build()) |                         .build()) | ||||||
|   | |||||||
| @@ -0,0 +1,312 @@ | |||||||
|  | package net.hostsharing.hsadminng.hs.migration; | ||||||
|  |  | ||||||
|  | import com.opencsv.CSVParserBuilder; | ||||||
|  | import com.opencsv.CSVReader; | ||||||
|  | import com.opencsv.CSVReaderBuilder; | ||||||
|  | import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; | ||||||
|  | import net.hostsharing.hsadminng.rbac.rbacobject.RbacObject; | ||||||
|  | import net.hostsharing.hsadminng.rbac.test.JpaAttempt; | ||||||
|  | import org.junit.jupiter.api.extension.BeforeEachCallback; | ||||||
|  | import org.junit.jupiter.api.extension.ExtensionContext; | ||||||
|  | import org.junit.jupiter.api.extension.TestWatcher; | ||||||
|  | import org.springframework.beans.factory.annotation.Autowired; | ||||||
|  | import org.springframework.beans.factory.annotation.Value; | ||||||
|  | import org.springframework.boot.test.mock.mockito.MockBean; | ||||||
|  | import org.springframework.transaction.support.TransactionTemplate; | ||||||
|  |  | ||||||
|  | import jakarta.persistence.EntityManager; | ||||||
|  | import jakarta.persistence.PersistenceContext; | ||||||
|  | import jakarta.servlet.http.HttpServletRequest; | ||||||
|  | import jakarta.validation.constraints.NotNull; | ||||||
|  |  | ||||||
|  | import java.io.BufferedReader; | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  | import java.io.Reader; | ||||||
|  | import java.io.StringReader; | ||||||
|  | import java.io.StringWriter; | ||||||
|  | import java.math.BigDecimal; | ||||||
|  | import java.time.LocalDate; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.TreeMap; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import static java.lang.Boolean.parseBoolean; | ||||||
|  | import static java.util.Arrays.stream; | ||||||
|  | import static java.util.Objects.requireNonNull; | ||||||
|  | import static java.util.Optional.ofNullable; | ||||||
|  | import static org.apache.commons.lang3.StringUtils.isNotBlank; | ||||||
|  | import static org.assertj.core.api.Assertions.assertThat; | ||||||
|  | import static org.assertj.core.api.Assumptions.assumeThat; | ||||||
|  |  | ||||||
|  | public class CsvDataImport extends ContextBasedTest { | ||||||
|  |  | ||||||
|  |     public static final String TEST_DATA_MIGRATION_DATA_PATH = "migration"; | ||||||
|  |     public static final String MIGRATION_DATA_PATH = ofNullable(System.getenv("HSADMINNG_MIGRATION_DATA_PATH")) | ||||||
|  |             .orElse(TEST_DATA_MIGRATION_DATA_PATH); | ||||||
|  |  | ||||||
|  |     @Value("${spring.datasource.url}") | ||||||
|  |     protected String jdbcUrl; | ||||||
|  |  | ||||||
|  |     @Value("${spring.datasource.username}") | ||||||
|  |     protected String postgresAdminUser; | ||||||
|  |  | ||||||
|  |     @Value("${hsadminng.superuser}") | ||||||
|  |     protected String rbacSuperuser; | ||||||
|  |  | ||||||
|  |     @PersistenceContext | ||||||
|  |     EntityManager em; | ||||||
|  |  | ||||||
|  |     @Autowired | ||||||
|  |     TransactionTemplate txTemplate; | ||||||
|  |  | ||||||
|  |     @Autowired | ||||||
|  |     JpaAttempt jpaAttempt; | ||||||
|  |  | ||||||
|  |     @MockBean | ||||||
|  |     HttpServletRequest request; | ||||||
|  |  | ||||||
|  |     private static final List<AssertionError> errors = new ArrayList<>(); | ||||||
|  |  | ||||||
|  |     public List<String[]> readAllLines(Reader reader) throws Exception { | ||||||
|  |  | ||||||
|  |         final var parser = new CSVParserBuilder() | ||||||
|  |                 .withSeparator(';') | ||||||
|  |                 .withQuoteChar('"') | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         final var filteredReader = skippingEmptyAndCommentLines(reader); | ||||||
|  |         try (CSVReader csvReader = new CSVReaderBuilder(filteredReader) | ||||||
|  |                 .withCSVParser(parser) | ||||||
|  |                 .build()) { | ||||||
|  |             return csvReader.readAll(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static Reader skippingEmptyAndCommentLines(Reader reader) throws IOException { | ||||||
|  |         try (var bufferedReader = new BufferedReader(reader); | ||||||
|  |              StringWriter writer = new StringWriter()) { | ||||||
|  |  | ||||||
|  |             String line; | ||||||
|  |             while ((line = bufferedReader.readLine()) != null) { | ||||||
|  |                 if (!line.isBlank() && !line.startsWith("#")) { | ||||||
|  |                     writer.write(line); | ||||||
|  |                     writer.write("\n"); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return new StringReader(writer.toString()); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected static String[] justHeader(final List<String[]> lines) { | ||||||
|  |         return stream(lines.getFirst()).map(String::trim).toArray(String[]::new); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected Reader resourceReader(@NotNull final String resourcePath) { | ||||||
|  |         return new InputStreamReader(requireNonNull(getClass().getClassLoader().getResourceAsStream(resourcePath))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected List<String[]> withoutHeader(final List<String[]> records) { | ||||||
|  |         return records.subList(1, records.size()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String[] trimAll(final String[] record) { | ||||||
|  |         for (int i = 0; i < record.length; ++i) { | ||||||
|  |             if (record[i] != null) { | ||||||
|  |                 record[i] = record[i].trim(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return record; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public <T extends RbacObject> T persist(final Integer id, final T entity) { | ||||||
|  |         try { | ||||||
|  |             final var asString = entity.toString(); | ||||||
|  |             if ( asString.contains("'null null, null'") || asString.equals("person()")) { | ||||||
|  |                 System.err.println("skipping to persist empty record-id " + id +  " #" + entity.hashCode() + ": " + entity); | ||||||
|  |                 return entity; | ||||||
|  |             } | ||||||
|  |             //System.out.println("persisting #" + entity.hashCode() + ": " + entity); | ||||||
|  |             em.persist(entity); | ||||||
|  |             // uncomment for debugging purposes | ||||||
|  |             // em.flush(); // makes it slow, but produces better error messages | ||||||
|  |             // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); | ||||||
|  |         } catch (Exception exc) { | ||||||
|  |             System.err.println("failed to persist #" + entity.hashCode() + ": " + entity); | ||||||
|  |             System.err.println(exc); | ||||||
|  |         } | ||||||
|  |         return entity; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected <E> String toFormattedString(final Map<Integer, E> map) { | ||||||
|  |         if ( map.isEmpty() ) { | ||||||
|  |             return "{}"; | ||||||
|  |         } | ||||||
|  |         return "{\n" + | ||||||
|  |                 map.keySet().stream() | ||||||
|  |                         .map(id -> "   " + id + "=" + map.get(id).toString()) | ||||||
|  |                         .map(e -> e.replaceAll("\n    ", " ").replace("\n", "")) | ||||||
|  |                         .sorted() | ||||||
|  |                         .collect(Collectors.joining(",\n")) + | ||||||
|  |                 "\n}\n"; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void deleteTestDataFromHsOfficeTables() { | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             // TODO.perf: could we instead skip creating test-data based on an env var? | ||||||
|  |             em.createNativeQuery("delete from hs_hosting_asset where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_booking_item where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_booking_project where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_coopassetstransaction where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_coopassetstransaction_legacy_id where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_coopsharestransaction where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_coopsharestransaction_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(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_debitor where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_partner where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_relation where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_contact where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from hs_office_person where true").executeUpdate(); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void resetHsOfficeSequences() { | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             em.createNativeQuery("alter sequence hs_office_contact_legacy_id_seq restart with 1000000000;").executeUpdate(); | ||||||
|  |             em.createNativeQuery("alter sequence hs_office_coopassetstransaction_legacy_id_seq restart with 1000000000;") | ||||||
|  |                     .executeUpdate(); | ||||||
|  |             em.createNativeQuery("alter sequence public.hs_office_coopsharestransaction_legacy_id_seq restart with 1000000000;") | ||||||
|  |                     .executeUpdate(); | ||||||
|  |             em.createNativeQuery("alter sequence public.hs_office_partner_legacy_id_seq restart with 1000000000;") | ||||||
|  |                     .executeUpdate(); | ||||||
|  |             em.createNativeQuery("alter sequence public.hs_office_sepamandate_legacy_id_seq restart with 1000000000;") | ||||||
|  |                     .executeUpdate(); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void deleteFromTestTables() { | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             em.createNativeQuery("delete from test_domain where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from test_package where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from test_customer where true").executeUpdate(); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected void deleteFromRbacTables() { | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             em.createNativeQuery("delete from rbacuser_rv where name not like 'superuser-%'").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from tx_journal where true").executeUpdate(); | ||||||
|  |             em.createNativeQuery("delete from tx_context where true").executeUpdate(); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void logError(final Runnable assertion) { | ||||||
|  |         try { | ||||||
|  |             assertion.run(); | ||||||
|  |         } catch (final AssertionError exc) { | ||||||
|  |             errors.add(exc); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void logErrors() { | ||||||
|  |         assumeThat(errors).isEmpty(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class Columns { | ||||||
|  |  | ||||||
|  |     private final List<String> columnNames; | ||||||
|  |  | ||||||
|  |     public Columns(final String[] header) { | ||||||
|  |         columnNames = List.of(header); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     int indexOf(final String columnName) { | ||||||
|  |         int index = columnNames.indexOf(columnName); | ||||||
|  |         if (index < 0) { | ||||||
|  |             throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); | ||||||
|  |         } | ||||||
|  |         return index; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class Record { | ||||||
|  |  | ||||||
|  |     private final Columns columns; | ||||||
|  |     private final String[] row; | ||||||
|  |  | ||||||
|  |     public Record(final Columns columns, final String[] row) { | ||||||
|  |         this.columns = columns; | ||||||
|  |         this.row = row; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     String getString(final String columnName) { | ||||||
|  |         return row[columns.indexOf(columnName)]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     boolean isEmpty(final String columnName) { | ||||||
|  |         final String value = getString(columnName); | ||||||
|  |         return value == null || value.isBlank(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     boolean getBoolean(final String columnName) { | ||||||
|  |         final String value = getString(columnName); | ||||||
|  |         return isNotBlank(value) && | ||||||
|  |                 ( parseBoolean(value.trim()) || value.trim().startsWith("t")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Integer getInteger(final String columnName) { | ||||||
|  |         final String value = getString(columnName); | ||||||
|  |         return isNotBlank(value) ? Integer.parseInt(value.trim()) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     BigDecimal getBigDecimal(final String columnName) { | ||||||
|  |         final String value = getString(columnName); | ||||||
|  |         if (isNotBlank(value)) { | ||||||
|  |             return new BigDecimal(value); | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     LocalDate getLocalDate(final String columnName) { | ||||||
|  |         final String dateString = getString(columnName); | ||||||
|  |         if (isNotBlank(dateString)) { | ||||||
|  |             return LocalDate.parse(dateString); | ||||||
|  |         } | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { | ||||||
|  |  | ||||||
|  |     private static boolean previousTestsPassed = true; | ||||||
|  |  | ||||||
|  |     public void testFailed(ExtensionContext context, Throwable cause) { | ||||||
|  |         previousTestsPassed = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void beforeEach(final ExtensionContext extensionContext) { | ||||||
|  |         assumeThat(previousTestsPassed).isTrue(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class WriteOnceMap<K, V> extends TreeMap<K, V> { | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public V put(final K k, final V v) { | ||||||
|  |         assertThat(containsKey(k)).describedAs("overwriting " + get(k) + " index " + k + " with " + v).isFalse(); | ||||||
|  |         return super.put(k, v); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,620 @@ | |||||||
|  | package net.hostsharing.hsadminng.hs.migration; | ||||||
|  |  | ||||||
|  | import net.hostsharing.hsadminng.context.Context; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorEntity; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.item.validators.HsBookingItemEntityValidatorRegistry; | ||||||
|  | import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectEntity; | ||||||
|  | import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity; | ||||||
|  | import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType; | ||||||
|  | import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor; | ||||||
|  | import net.hostsharing.hsadminng.rbac.test.JpaAttempt; | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | import org.junit.jupiter.api.MethodOrderer; | ||||||
|  | import org.junit.jupiter.api.Order; | ||||||
|  | import org.junit.jupiter.api.Tag; | ||||||
|  | import org.junit.jupiter.api.Test; | ||||||
|  | import org.junit.jupiter.api.TestMethodOrder; | ||||||
|  | import org.junit.jupiter.api.extension.ExtendWith; | ||||||
|  | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | ||||||
|  | import org.springframework.context.annotation.Import; | ||||||
|  | import org.springframework.test.annotation.Commit; | ||||||
|  | import org.springframework.test.annotation.DirtiesContext; | ||||||
|  |  | ||||||
|  | import java.io.Reader; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.concurrent.atomic.AtomicReference; | ||||||
|  | import java.util.function.Function; | ||||||
|  |  | ||||||
|  | import static java.util.Arrays.stream; | ||||||
|  | import static java.util.Optional.ofNullable; | ||||||
|  | import static java.util.stream.Collectors.toMap; | ||||||
|  | import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER; | ||||||
|  | import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.IPV4_NUMBER; | ||||||
|  | import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER; | ||||||
|  | import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE; | ||||||
|  | import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; | ||||||
|  | import static org.assertj.core.api.Assertions.assertThat; | ||||||
|  | import static org.assertj.core.api.Assumptions.assumeThat; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * This 'test' includes the complete legacy 'office' data import. | ||||||
|  |  * | ||||||
|  |  * There is no code in 'main' because the import is not needed a normal runtime. | ||||||
|  |  * There is some test data in Java resources to verify the data conversion. | ||||||
|  |  * For a real import a main method will be added later | ||||||
|  |  * which reads CSV files from the file system. | ||||||
|  |  * | ||||||
|  |  * When run on a Hostsharing database, it needs the following settings (hsh99_... just examples). | ||||||
|  |  * | ||||||
|  |  * In a real Hostsharing environment, these are created via (the old) hsadmin: | ||||||
|  |  | ||||||
|  |     CREATE USER hsh99_admin WITH PASSWORD 'password'; | ||||||
|  |     CREATE DATABASE hsh99_hsadminng  ENCODING 'UTF8' TEMPLATE template0; | ||||||
|  |     REVOKE ALL ON DATABASE hsh99_hsadminng FROM public; -- why does hsadmin do that? | ||||||
|  |     ALTER DATABASE hsh99_hsadminng OWNER TO hsh99_admin; | ||||||
|  |  | ||||||
|  |     CREATE USER hsh99_restricted WITH PASSWORD 'password'; | ||||||
|  |  | ||||||
|  |     \c hsh99_hsadminng | ||||||
|  |  | ||||||
|  |     GRANT ALL PRIVILEGES ON SCHEMA public to hsh99_admin; | ||||||
|  |  | ||||||
|  |  * Additionally, we need these settings (because the Hostsharing DB-Admin has no CREATE right): | ||||||
|  |  | ||||||
|  |     CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; | ||||||
|  |  | ||||||
|  |     -- maybe something like that is needed for the 2nd user | ||||||
|  |     -- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public to hsh99_restricted; | ||||||
|  |  | ||||||
|  |  * Then copy the file .tc-environment to a file named .environment (excluded from git) and fill in your specific values. | ||||||
|  |  | ||||||
|  |  * To finally import the office data, run: | ||||||
|  |  * | ||||||
|  |  *   gw-importHostingAssets # comes from .aliases file and uses .environment | ||||||
|  |  */ | ||||||
|  | @Tag("importHostingAssets") | ||||||
|  | @DataJpaTest(properties = { | ||||||
|  |         "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", | ||||||
|  |         "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}", | ||||||
|  |         "spring.datasource.password=${HSADMINNG_POSTGRES_ADMIN_PASSWORD:password}", | ||||||
|  |         "hsadminng.superuser=${HSADMINNG_SUPERUSER:superuser-alex@hostsharing.net}" | ||||||
|  | }) | ||||||
|  | @DirtiesContext | ||||||
|  | @Import({ Context.class, JpaAttempt.class }) | ||||||
|  | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) | ||||||
|  | @ExtendWith(OrderedDependedTestsExtension.class) | ||||||
|  | public class ImportHostingAssets extends ImportOfficeData { | ||||||
|  |  | ||||||
|  |     static final Integer IP_NUMBER_ID_OFFSET = 1000000; | ||||||
|  |     static final Integer HIVE_ID_OFFSET = 2000000; | ||||||
|  |     static final Integer PACKET_ID_OFFSET = 3000000; | ||||||
|  |  | ||||||
|  |     record Hive(int hive_id, String hive_name, int inet_addr_id, AtomicReference<HsHostingAssetEntity> serverRef) {} | ||||||
|  |  | ||||||
|  |     static Map<Integer, HsBookingProjectEntity> bookingProjects = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, HsBookingItemEntity> bookingItems = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, Hive> hives = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, HsHostingAssetEntity> hostingAssets = new WriteOnceMap<>(); // TODO.impl: separate maps for each type? | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(11010) | ||||||
|  |     void createBookingProjects() { | ||||||
|  |         debitors.forEach((id, debitor) -> { | ||||||
|  |             bookingProjects.put(id, HsBookingProjectEntity.builder() | ||||||
|  |                     .caption(debitor.getDefaultPrefix() + " default project") | ||||||
|  |                     .debitor(em.find(HsBookingDebitorEntity.class, debitor.getUuid())) | ||||||
|  |                     .build()); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(12010) | ||||||
|  |     void importIpNumbers() { | ||||||
|  |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/inet_addr.csv")) { | ||||||
|  |             final var lines = readAllLines(reader); | ||||||
|  |             importIpNumbers(justHeader(lines), withoutHeader(lines)); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(12019) | ||||||
|  |     void verifyIpNumbers() { | ||||||
|  |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  |  | ||||||
|  |         // no contacts yet => mostly null values | ||||||
|  |         assertThat(firstOfEachType(5, IPV4_NUMBER)).isEqualToIgnoringWhitespace(""" | ||||||
|  |                 { | ||||||
|  |                    1000363=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.34), | ||||||
|  |                    1000381=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.52), | ||||||
|  |                    1000402=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.73), | ||||||
|  |                    1000433=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.104), | ||||||
|  |                    1000457=HsHostingAssetEntity(IPV4_NUMBER, 83.223.95.128) | ||||||
|  |                 } | ||||||
|  |                 """); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(12030) | ||||||
|  |     void importHives() { | ||||||
|  |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/hive.csv")) { | ||||||
|  |             final var lines = readAllLines(reader); | ||||||
|  |             importHives(justHeader(lines), withoutHeader(lines)); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(12039) | ||||||
|  |     void verifyHives() { | ||||||
|  |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  |  | ||||||
|  |         // no contacts yet => mostly null values | ||||||
|  |         assertThat(toFormattedString(first(5, hives))).isEqualToIgnoringWhitespace(""" | ||||||
|  |                 { | ||||||
|  |                    2000001=Hive[hive_id=1, hive_name=h00, inet_addr_id=358, serverRef=null], | ||||||
|  |                    2000002=Hive[hive_id=2, hive_name=h01, inet_addr_id=359, serverRef=null], | ||||||
|  |                    2000004=Hive[hive_id=4, hive_name=h02, inet_addr_id=360, serverRef=null], | ||||||
|  |                    2000007=Hive[hive_id=7, hive_name=h03, inet_addr_id=361, serverRef=null], | ||||||
|  |                    2000013=Hive[hive_id=13, hive_name=h04, inet_addr_id=430, serverRef=null] | ||||||
|  |                 } | ||||||
|  |                 """); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(13000) | ||||||
|  |     void importPackets() { | ||||||
|  |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/packet.csv")) { | ||||||
|  |             final var lines = readAllLines(reader); | ||||||
|  |             importPackets(justHeader(lines), withoutHeader(lines)); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(13009) | ||||||
|  |     void verifyPackets() { | ||||||
|  |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  |  | ||||||
|  |         assertThat(firstOfEachType(3, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)).isEqualToIgnoringWhitespace(""" | ||||||
|  |                 { | ||||||
|  |                    3000630=HsHostingAssetEntity(MANAGED_WEBSPACE,   hsh00, HA hsh00,  MANAGED_SERVER:vm1050,    D-1000000:hsh default project:BI hsh00), | ||||||
|  |                    3000968=HsHostingAssetEntity(MANAGED_SERVER,    vm1061, HA vm1061,                           D-1015200:rar default project:BI vm1061), | ||||||
|  |                    3000978=HsHostingAssetEntity(MANAGED_SERVER,    vm1050, HA vm1050,                           D-1000000:hsh default project:BI vm1050), | ||||||
|  |                    3001061=HsHostingAssetEntity(MANAGED_SERVER,    vm1068, HA vm1068,                           D-1000300:mim default project:BI vm1068), | ||||||
|  |                    3001094=HsHostingAssetEntity(MANAGED_WEBSPACE,   lug00, HA lug00,  MANAGED_SERVER:vm1068,    D-1000300:mim default project:BI lug00), | ||||||
|  |                    3001112=HsHostingAssetEntity(MANAGED_WEBSPACE,   mim00, HA mim00,  MANAGED_SERVER:vm1068,    D-1000300:mim default project:BI mim00), | ||||||
|  |                    3023611=HsHostingAssetEntity(CLOUD_SERVER,      vm2097, HA vm2097,                           D-1101800:wws default project:BI vm2097) | ||||||
|  |                 } | ||||||
|  |                 """); | ||||||
|  |         assertThat(firstOfEachType( | ||||||
|  |                 3, | ||||||
|  |                 HsBookingItemType.CLOUD_SERVER, | ||||||
|  |                 HsBookingItemType.MANAGED_SERVER, | ||||||
|  |                 HsBookingItemType.MANAGED_WEBSPACE)).isEqualToIgnoringWhitespace(""" | ||||||
|  |                 { | ||||||
|  |                    3000630=HsBookingItemEntity(D-1000000:hsh default project, MANAGED_WEBSPACE, [2001-06-01,), BI hsh00), | ||||||
|  |                    3000968=HsBookingItemEntity(D-1015200:rar default project, MANAGED_SERVER, [2013-04-01,), BI vm1061), | ||||||
|  |                    3000978=HsBookingItemEntity(D-1000000:hsh default project, MANAGED_SERVER, [2013-04-01,), BI vm1050), | ||||||
|  |                    3001061=HsBookingItemEntity(D-1000300:mim default project, MANAGED_SERVER, [2013-08-19,), BI vm1068), | ||||||
|  |                    3001094=HsBookingItemEntity(D-1000300:mim default project, MANAGED_WEBSPACE, [2013-09-10,), BI lug00), | ||||||
|  |                    3001112=HsBookingItemEntity(D-1000300:mim default project, MANAGED_WEBSPACE, [2013-09-17,), BI mim00), | ||||||
|  |                    3023611=HsBookingItemEntity(D-1101800:wws default project, CLOUD_SERVER, [2022-08-10,), BI vm2097) | ||||||
|  |                 } | ||||||
|  |                 """); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(13010) | ||||||
|  |     void importPacketComponents() { | ||||||
|  |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/hosting/packet_component.csv")) { | ||||||
|  |             final var lines = readAllLines(reader); | ||||||
|  |             importPacketComponents(justHeader(lines), withoutHeader(lines)); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(13019) | ||||||
|  |     void verifyPacketComponents() { | ||||||
|  |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  |  | ||||||
|  |         // no contacts yet => mostly null values | ||||||
|  |         assertThat(firstOfEachType(5, CLOUD_SERVER, MANAGED_SERVER, MANAGED_WEBSPACE)) | ||||||
|  |                 .isEqualToIgnoringWhitespace(""" | ||||||
|  |                         { | ||||||
|  |                            3000630=HsHostingAssetEntity(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00), | ||||||
|  |                            3000968=HsHostingAssetEntity(MANAGED_SERVER, vm1061, HA vm1061, D-1015200:rar default project:BI vm1061), | ||||||
|  |                            3000978=HsHostingAssetEntity(MANAGED_SERVER, vm1050, HA vm1050, D-1000000:hsh default project:BI vm1050), | ||||||
|  |                            3001061=HsHostingAssetEntity(MANAGED_SERVER, vm1068, HA vm1068, D-1000300:mim default project:BI vm1068), | ||||||
|  |                            3001094=HsHostingAssetEntity(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00), | ||||||
|  |                            3001112=HsHostingAssetEntity(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00), | ||||||
|  |                            3001447=HsHostingAssetEntity(MANAGED_SERVER, vm1093, HA vm1093, D-1000000:hsh default project:BI vm1093), | ||||||
|  |                            3019959=HsHostingAssetEntity(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00), | ||||||
|  |                            3023611=HsHostingAssetEntity(CLOUD_SERVER, vm2097, HA vm2097, D-1101800:wws default project:BI vm2097) | ||||||
|  |                         } | ||||||
|  |                         """); | ||||||
|  |         assertThat(firstOfEachType( | ||||||
|  |                 5, | ||||||
|  |                 HsBookingItemType.CLOUD_SERVER, | ||||||
|  |                 HsBookingItemType.MANAGED_SERVER, | ||||||
|  |                 HsBookingItemType.MANAGED_WEBSPACE)) | ||||||
|  |                 .isEqualToIgnoringWhitespace(""" | ||||||
|  |                         { | ||||||
|  |                            3000630=HsBookingItemEntity(D-1000000:hsh default project, MANAGED_WEBSPACE, [2001-06-01,), BI hsh00, { "HDD": 10, "Multi": 25, "SLA-Platform": "EXT24H", "SSD": 16, "Traffic": 50}), | ||||||
|  |                            3000968=HsBookingItemEntity(D-1015200:rar default project, MANAGED_SERVER, [2013-04-01,), BI vm1061, { "CPU": 6, "HDD": 250, "RAM": 14, "SLA-EMail": true, "SLA-Maria": true, "SLA-Office": true, "SLA-PgSQL": true, "SLA-Platform": "EXT4H", "SLA-Web": true, "SSD": 375, "Traffic": 250}), | ||||||
|  |                            3000978=HsBookingItemEntity(D-1000000:hsh default project, MANAGED_SERVER, [2013-04-01,), BI vm1050, { "CPU": 4, "HDD": 250, "RAM": 32, "SLA-EMail": true, "SLA-Maria": true, "SLA-Office": true, "SLA-PgSQL": true, "SLA-Platform": "EXT4H", "SLA-Web": true, "SSD": 150, "Traffic": 250}), | ||||||
|  |                            3001061=HsBookingItemEntity(D-1000300:mim default project, MANAGED_SERVER, [2013-08-19,), BI vm1068, { "CPU": 2, "HDD": 250, "RAM": 4, "SLA-EMail": true, "SLA-Maria": true, "SLA-Office": true, "SLA-PgSQL": true, "SLA-Platform": "EXT2H", "SLA-Web": true, "Traffic": 250}), | ||||||
|  |                            3001094=HsBookingItemEntity(D-1000300:mim default project, MANAGED_WEBSPACE, [2013-09-10,), BI lug00, { "Multi": 5, "SLA-Platform": "EXT24H", "SSD": 1, "Traffic": 10}), | ||||||
|  |                            3001112=HsBookingItemEntity(D-1000300:mim default project, MANAGED_WEBSPACE, [2013-09-17,), BI mim00, { "Multi": 5, "SLA-Platform": "EXT24H", "SSD": 3, "Traffic": 20}), | ||||||
|  |                            3001447=HsBookingItemEntity(D-1000000:hsh default project, MANAGED_SERVER, [2014-11-28,), BI vm1093, { "CPU": 6, "HDD": 500, "RAM": 16, "SLA-EMail": true, "SLA-Maria": true, "SLA-Office": true, "SLA-PgSQL": true, "SLA-Platform": "EXT4H", "SLA-Web": true, "SSD": 300, "Traffic": 250}), | ||||||
|  |                            3019959=HsBookingItemEntity(D-1101900:dph default project, MANAGED_WEBSPACE, [2021-06-02,), BI dph00, { "Multi": 1, "SLA-Platform": "EXT24H", "SSD": 25, "Traffic": 20}), | ||||||
|  |                            3023611=HsBookingItemEntity(D-1101800:wws default project, CLOUD_SERVER, [2022-08-10,), BI vm2097, { "CPU": 8, "RAM": 12, "SLA-Infrastructure": "EXT4H", "SSD": 25, "Traffic": 250}) | ||||||
|  |                         } | ||||||
|  |                         """); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(11400) | ||||||
|  |     void validateBookingItems() { | ||||||
|  |         bookingItems.forEach((id, bi) -> { | ||||||
|  |             try { | ||||||
|  |                 HsBookingItemEntityValidatorRegistry.validated(bi); | ||||||
|  |             } catch (final Exception exc) { | ||||||
|  |                 System.err.println("validation failed for id:" + id + "( " + bi + "): " + exc.getMessage()); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(11410) | ||||||
|  |     void validateHostingAssets() { | ||||||
|  |         hostingAssets.forEach((id, ha) -> { | ||||||
|  |             try { | ||||||
|  |                 new HostingAssetEntitySaveProcessor(ha) | ||||||
|  |                         .preprocessEntity() | ||||||
|  |                         .validateEntity(); | ||||||
|  |             } catch (final Exception exc) { | ||||||
|  |                 System.err.println("validation failed for id:" + id + "( " + ha + "): " + exc.getMessage()); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(19000) | ||||||
|  |     @Commit | ||||||
|  |     void persistHostingAssetEntities() { | ||||||
|  |  | ||||||
|  |         System.out.println("PERSISTING hosting-assets to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); | ||||||
|  |  | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             bookingProjects.forEach(this::persist); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |  | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             bookingItems.forEach(this::persistRecursively); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |  | ||||||
|  |         persistHostingAssetsOfType(CLOUD_SERVER); | ||||||
|  |         persistHostingAssetsOfType(MANAGED_SERVER); | ||||||
|  |         persistHostingAssetsOfType(MANAGED_WEBSPACE); | ||||||
|  |         persistHostingAssetsOfType(IPV4_NUMBER); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(99999) | ||||||
|  |     void logErrors() { | ||||||
|  |         super.logErrors(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void persistRecursively(final Integer key, final HsBookingItemEntity bi) { | ||||||
|  |         if (bi.getParentItem() != null) { | ||||||
|  |             persistRecursively(key, HsBookingItemEntityValidatorRegistry.validated(bi.getParentItem())); | ||||||
|  |         } | ||||||
|  |         persist(key, HsBookingItemEntityValidatorRegistry.validated(bi)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void persistHostingAssetsOfType(final HsHostingAssetType hsHostingAssetType) { | ||||||
|  |         jpaAttempt.transacted(() -> { | ||||||
|  |             context(rbacSuperuser); | ||||||
|  |             hostingAssets.forEach((key, ha) -> { | ||||||
|  |                     if (ha.getType() == hsHostingAssetType) { | ||||||
|  |                         new HostingAssetEntitySaveProcessor(ha) | ||||||
|  |                                 .preprocessEntity() | ||||||
|  |                                 .validateEntity() | ||||||
|  |                                 .prepareForSave() | ||||||
|  |                                 .saveUsing(entity -> persist(key, entity)) | ||||||
|  |                                 .validateContext(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             ); | ||||||
|  |         }).assertSuccessful(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void importIpNumbers(final String[] header, final List<String[]> records) { | ||||||
|  |         final var columns = new Columns(header); | ||||||
|  |         records.stream() | ||||||
|  |                 .map(this::trimAll) | ||||||
|  |                 .map(row -> new Record(columns, row)) | ||||||
|  |                 .forEach(rec -> { | ||||||
|  |                     final var ipNumber = HsHostingAssetEntity.builder() | ||||||
|  |                             .type(IPV4_NUMBER) | ||||||
|  |                             .identifier(rec.getString("inet_addr")) | ||||||
|  |                             .caption(rec.getString("description")) | ||||||
|  |                             .build(); | ||||||
|  |                     hostingAssets.put(IP_NUMBER_ID_OFFSET + rec.getInteger("inet_addr_id"), ipNumber); | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void importHives(final String[] header, final List<String[]> records) { | ||||||
|  |         final var columns = new Columns(header); | ||||||
|  |         records.stream() | ||||||
|  |                 .map(this::trimAll) | ||||||
|  |                 .map(row -> new Record(columns, row)) | ||||||
|  |                 .forEach(rec -> { | ||||||
|  |                     final var hive_id = rec.getInteger("hive_id"); | ||||||
|  |                     final var hive = new Hive( | ||||||
|  |                             hive_id, | ||||||
|  |                             rec.getString("hive_name"), | ||||||
|  |                             rec.getInteger("inet_addr_id"), | ||||||
|  |                             new AtomicReference<>()); | ||||||
|  |                     hives.put(HIVE_ID_OFFSET + hive_id, hive); | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void importPackets(final String[] header, final List<String[]> records) { | ||||||
|  |         final var columns = new Columns(header); | ||||||
|  |         records.stream() | ||||||
|  |                 .map(this::trimAll) | ||||||
|  |                 .map(row -> new Record(columns, row)) | ||||||
|  |                 .forEach(rec -> { | ||||||
|  |                     final var packet_id = rec.getInteger("packet_id"); | ||||||
|  |                     final var basepacket_code = rec.getString("basepacket_code"); | ||||||
|  |                     final var packet_name = rec.getString("packet_name"); | ||||||
|  |                     final var bp_id = rec.getInteger("bp_id"); | ||||||
|  |                     final var hive_id = rec.getInteger("hive_id"); | ||||||
|  |                     final var created = rec.getLocalDate("created"); | ||||||
|  |                     final var cancelled = rec.getLocalDate("cancelled"); | ||||||
|  |                     final var cur_inet_addr_id = rec.getInteger("cur_inet_addr_id"); | ||||||
|  |                     final var old_inet_addr_id = rec.getInteger("old_inet_addr_id"); | ||||||
|  |                     final var free = rec.getBoolean("free"); | ||||||
|  |  | ||||||
|  |                     assertThat(old_inet_addr_id) | ||||||
|  |                             .as("packet.old_inet_addr_id not supported, but is not null for " + packet_name) | ||||||
|  |                             .isNull(); | ||||||
|  |  | ||||||
|  |                     final var biType = determineBiType(basepacket_code); | ||||||
|  |                     final var bookingItem = HsBookingItemEntity.builder() | ||||||
|  |                             .type(biType) | ||||||
|  |                             .caption("BI " + packet_name) | ||||||
|  |                             .project(bookingProjects.get(bp_id)) | ||||||
|  |                             .validity(toPostgresDateRange(created, cancelled)) | ||||||
|  |                             .build(); | ||||||
|  |                     bookingItems.put(PACKET_ID_OFFSET + packet_id, bookingItem); | ||||||
|  |                     final var haType = determineHaType(basepacket_code); | ||||||
|  |  | ||||||
|  |                     logError(() -> assertThat(!free || haType == MANAGED_WEBSPACE || bookingItem.getRelatedProject().getDebitor().getDefaultPrefix().equals("hsh")) | ||||||
|  |                             .as("packet.free only supported for Hostsharing-Assets and ManagedWebspace in customer-ManagedServer, but is set for " + packet_name) | ||||||
|  |                             .isTrue()); | ||||||
|  |  | ||||||
|  |                     final var asset = HsHostingAssetEntity.builder() | ||||||
|  |                             .isLoaded(haType == MANAGED_WEBSPACE) // this turns off identifier validation to accept former default prefixes | ||||||
|  |                             .type(haType) | ||||||
|  |                             .identifier(packet_name) | ||||||
|  |                             .bookingItem(bookingItem) | ||||||
|  |                             .caption("HA " + packet_name) | ||||||
|  |                             .build(); | ||||||
|  |                     hostingAssets.put(PACKET_ID_OFFSET + packet_id, asset); | ||||||
|  |                     if (haType == MANAGED_SERVER) { | ||||||
|  |                         hive(hive_id).serverRef.set(asset); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (cur_inet_addr_id != null) { | ||||||
|  |                         ipNumber(cur_inet_addr_id).setAssignedToAsset(asset); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |         // once we know all hosting assets, we can set the parentAsset for managed webspaces | ||||||
|  |         records.stream() | ||||||
|  |                 .map(this::trimAll) | ||||||
|  |                 .map(row -> new Record(columns, row)) | ||||||
|  |                 .forEach(rec -> { | ||||||
|  |                     final var packet_id = rec.getInteger("packet_id"); | ||||||
|  |                     final var basepacket_code = rec.getString("basepacket_code"); | ||||||
|  |                     final var hive_id = rec.getInteger("hive_id"); | ||||||
|  |  | ||||||
|  |                     final var haType = determineHaType(basepacket_code); | ||||||
|  |                     if (haType == MANAGED_WEBSPACE) { | ||||||
|  |                         final var managedWebspace = pac(packet_id); | ||||||
|  |                         final var parentAsset = hive(hive_id).serverRef.get(); | ||||||
|  |                         managedWebspace.setParentAsset(parentAsset); | ||||||
|  |                         managedWebspace.getBookingItem().setParentItem(parentAsset.getBookingItem()); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private void importPacketComponents(final String[] header, final List<String[]> records) { | ||||||
|  |         final var columns = new Columns(header); | ||||||
|  |         records.stream() | ||||||
|  |                 .map(this::trimAll) | ||||||
|  |                 .map(row -> new Record(columns, row)) | ||||||
|  |                 .forEach(rec -> { | ||||||
|  |                     // final var packet_component_id = rec.getInteger("packet_component_id"); not needed | ||||||
|  |                     final var packet_id = rec.getInteger("packet_id"); | ||||||
|  |                     final var quantity = rec.getInteger("quantity"); | ||||||
|  |                     final var basecomponent_code = rec.getString("basecomponent_code"); | ||||||
|  |                     // final var created = rec.getLocalDate("created"); TODO.spec: can we do without? | ||||||
|  |                     // final var cancelled = rec.getLocalDate("cancelled"); TODO.spec: can we do without? | ||||||
|  |                     Function<Integer, Object> convert = (v -> v); | ||||||
|  |  | ||||||
|  |                     final var asset = pac(packet_id); | ||||||
|  |                     final var name = switch (basecomponent_code) { | ||||||
|  |                         case "DAEMON" -> "Daemons"; | ||||||
|  |                         case "MULTI" -> "Multi"; | ||||||
|  |                         case "CPU" -> "CPU"; | ||||||
|  |                         case "RAM" -> returning("RAM", convert = v -> v/1024); | ||||||
|  |                         case "QUOTA" -> returning("SSD", convert = v -> v/1024); | ||||||
|  |                         case "STORAGE" -> returning("HDD", convert = v -> v/1024); | ||||||
|  |                         case "TRAFFIC" -> "Traffic"; | ||||||
|  |                         case "OFFICE" -> returning("Online Office Server", convert = v -> v == 1); | ||||||
|  |  | ||||||
|  |                         case "SLABASIC" -> switch (asset.getType()) { | ||||||
|  |                             case CLOUD_SERVER -> "SLA-Infrastructure"; | ||||||
|  |                             case MANAGED_SERVER -> "SLA-Platform"; | ||||||
|  |                             case MANAGED_WEBSPACE -> "SLA-Platform"; | ||||||
|  |                             default -> throw new IllegalArgumentException("SLABASIC not defined for " + asset.getType()); | ||||||
|  |                         }; | ||||||
|  |  | ||||||
|  |                         case "SLAINFR2H" -> "SLA-Infrastructure"; | ||||||
|  |                         case "SLAINFR4H" -> "SLA-Infrastructure"; | ||||||
|  |                         case "SLAINFR8H" -> "SLA-Infrastructure"; | ||||||
|  |  | ||||||
|  |                         case "SLAEXT24H" -> "SLA-Platform"; | ||||||
|  |  | ||||||
|  |                         case "SLAPLAT2H" -> "SLA-Platform"; | ||||||
|  |                         case "SLAPLAT4H" -> "SLA-Platform"; | ||||||
|  |                         case "SLAPLAT8H" -> "SLA-Platform"; | ||||||
|  |  | ||||||
|  |                         case "SLAWEB2H" -> "SLA-Web"; | ||||||
|  |                         case "SLAWEB4H" -> "SLA-Web"; | ||||||
|  |                         case "SLAWEB8H" -> "SLA-Web"; | ||||||
|  |  | ||||||
|  |                         case "SLAMAIL2H" -> "SLA-EMail"; | ||||||
|  |                         case "SLAMAIL4H" -> "SLA-EMail"; | ||||||
|  |                         case "SLAMAIL8H" -> "SLA-EMail"; | ||||||
|  |  | ||||||
|  |                         case "SLAMARIA2H" -> "SLA-Maria"; | ||||||
|  |                         case "SLAMARIA4H" -> "SLA-Maria"; | ||||||
|  |                         case "SLAMARIA8H" -> "SLA-Maria"; | ||||||
|  |  | ||||||
|  |                         case "SLAPGSQL2H" -> "SLA-PgSQL"; | ||||||
|  |                         case "SLAPGSQL4H" -> "SLA-PgSQL"; | ||||||
|  |                         case "SLAPGSQL8H" -> "SLA-PgSQL"; | ||||||
|  |  | ||||||
|  |                         case "SLAOFFIC2H" -> "SLA-Office"; | ||||||
|  |                         case "SLAOFFIC4H" -> "SLA-Office"; | ||||||
|  |                         case "SLAOFFIC8H" -> "SLA-Office"; | ||||||
|  |  | ||||||
|  |                         case "BANDWIDTH" -> "Bandwidth"; | ||||||
|  |                         default -> throw new IllegalArgumentException("unknown basecomponent_code: " + basecomponent_code); | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|  |                     if (name.equals("SLA-Infrastructure")) { | ||||||
|  |                         final var slaValue = switch (basecomponent_code) { | ||||||
|  |                             case "SLABASIC" -> "BASIC"; | ||||||
|  |                             case "SLAINFR2H" -> "EXT2H"; | ||||||
|  |                             case "SLAINFR4H" -> "EXT4H"; | ||||||
|  |                             case "SLAINFR8H" -> "EXT8H"; | ||||||
|  |                             default -> throw new IllegalArgumentException("unknown basecomponent_code: " + basecomponent_code); | ||||||
|  |                         }; | ||||||
|  |                         asset.getBookingItem().getResources().put(name, slaValue); | ||||||
|  |                     } else if (name.equals("SLA-Platform")) { | ||||||
|  |                         final var slaValue = switch (basecomponent_code) { | ||||||
|  |                             case "SLABASIC" -> "BASIC"; | ||||||
|  |                             case "SLAEXT24H" -> "EXT24H"; | ||||||
|  |                             case "SLAPLAT2H" -> "EXT2H"; | ||||||
|  |                             case "SLAPLAT4H" -> "EXT4H"; | ||||||
|  |                             case "SLAPLAT8H" -> "EXT8H"; | ||||||
|  |                             default -> throw new IllegalArgumentException("unknown basecomponent_code: " + basecomponent_code); | ||||||
|  |                         }; | ||||||
|  |                         if ( ofNullable(asset.getBookingItem().getResources().get(name)).map("BASIC"::equals).orElse(true) ) { | ||||||
|  |                             asset.getBookingItem().getResources().put(name, slaValue); | ||||||
|  |                         } | ||||||
|  |                     } else if (name.startsWith("SLA")) { | ||||||
|  |                         asset.getBookingItem().getResources().put(name, true); | ||||||
|  |                     } else if (quantity > 0) { | ||||||
|  |                         asset.getBookingItem().getResources().put(name, convert.apply(quantity)); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     <V> V returning(final V value, final Object... assignments) { | ||||||
|  |         return value; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static @NotNull HsBookingItemType determineBiType(final String basepacket_code) { | ||||||
|  |         return switch (basepacket_code) { | ||||||
|  |             case "SRV/CLD" -> HsBookingItemType.CLOUD_SERVER; | ||||||
|  |             case "SRV/MGD" -> HsBookingItemType.MANAGED_SERVER; | ||||||
|  |             case "PAC/WEB" -> HsBookingItemType.MANAGED_WEBSPACE; | ||||||
|  |             default -> throw new IllegalArgumentException( | ||||||
|  |                     "unknown basepacket_code: " + basepacket_code); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static @NotNull HsHostingAssetType determineHaType(final String basepacket_code) { | ||||||
|  |         return switch (basepacket_code) { | ||||||
|  |             case "SRV/CLD" -> CLOUD_SERVER; | ||||||
|  |             case "SRV/MGD" -> MANAGED_SERVER; | ||||||
|  |             case "PAC/WEB" -> MANAGED_WEBSPACE; | ||||||
|  |             default -> throw new IllegalArgumentException( | ||||||
|  |                     "unknown basepacket_code: " + basepacket_code); | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static HsHostingAssetEntity ipNumber(final Integer inet_addr_id) { | ||||||
|  |         return inet_addr_id != null ? hostingAssets.get(IP_NUMBER_ID_OFFSET + inet_addr_id) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static Hive hive(final Integer hive_id) { | ||||||
|  |         return hive_id != null ? hives.get(HIVE_ID_OFFSET + hive_id) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private static HsHostingAssetEntity pac(final Integer packet_id) { | ||||||
|  |         return packet_id != null ? hostingAssets.get(PACKET_ID_OFFSET + packet_id) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String firstOfEachType( | ||||||
|  |             final int maxCount, | ||||||
|  |             final HsHostingAssetType... types) { | ||||||
|  |         return toFormattedString(stream(types) | ||||||
|  |                 .flatMap(t -> | ||||||
|  |                         hostingAssets.entrySet().stream() | ||||||
|  |                                 .filter(hae -> hae.getValue().getType() == t) | ||||||
|  |                                 .limit(maxCount) | ||||||
|  |                 ) | ||||||
|  |                 .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private String firstOfEachType( | ||||||
|  |             final int maxCount, | ||||||
|  |             final HsBookingItemType... types) { | ||||||
|  |         return toFormattedString(stream(types) | ||||||
|  |                 .flatMap(t -> | ||||||
|  |                         bookingItems.entrySet().stream() | ||||||
|  |                                 .filter(bie -> bie.getValue().getType() == t) | ||||||
|  |                                 .limit(maxCount) | ||||||
|  |                 ) | ||||||
|  |                 .collect(toMap(Map.Entry::getKey, Map.Entry::getValue))); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Map<Integer, Object> first( | ||||||
|  |             final int maxCount, | ||||||
|  |             final Map<Integer, ?> entities) { | ||||||
|  |         return entities.entrySet().stream() | ||||||
|  |                 .limit(maxCount) | ||||||
|  |                 .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     protected void assumeThatWeAreExplicitlyImportingOfficeData() { | ||||||
|  |         assumeThat(false).isTrue(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected static boolean isImportingControlledTestData() { | ||||||
|  |         return MIGRATION_DATA_PATH.equals(TEST_DATA_MIGRATION_DATA_PATH); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     protected static void assumeThatWeAreImportingControlledTestData() { | ||||||
|  |         assumeThat(isImportingControlledTestData()).isTrue(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,10 +1,6 @@ | |||||||
| package net.hostsharing.hsadminng.hs.office.migration; | package net.hostsharing.hsadminng.hs.migration; | ||||||
| 
 | 
 | ||||||
| import com.opencsv.CSVParserBuilder; |  | ||||||
| import com.opencsv.CSVReader; |  | ||||||
| import com.opencsv.CSVReaderBuilder; |  | ||||||
| import net.hostsharing.hsadminng.context.Context; | import net.hostsharing.hsadminng.context.Context; | ||||||
| import net.hostsharing.hsadminng.rbac.context.ContextBasedTest; |  | ||||||
| import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; | import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; | ||||||
| import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; | import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity; | ||||||
| import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; | import net.hostsharing.hsadminng.hs.office.coopassets.HsOfficeCoopAssetsTransactionEntity; | ||||||
| @@ -26,34 +22,17 @@ import net.hostsharing.hsadminng.rbac.test.JpaAttempt; | |||||||
| import org.apache.commons.lang3.ArrayUtils; | import org.apache.commons.lang3.ArrayUtils; | ||||||
| import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||||
| import org.junit.jupiter.api.*; | import org.junit.jupiter.api.*; | ||||||
| import org.junit.jupiter.api.extension.BeforeEachCallback; |  | ||||||
| import org.junit.jupiter.api.extension.ExtendWith; | import org.junit.jupiter.api.extension.ExtendWith; | ||||||
| import org.junit.jupiter.api.extension.ExtensionContext; |  | ||||||
| import org.junit.jupiter.api.extension.TestWatcher; |  | ||||||
| import org.springframework.beans.factory.annotation.Autowired; |  | ||||||
| import org.springframework.beans.factory.annotation.Value; |  | ||||||
| import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; | ||||||
| import org.springframework.boot.test.mock.mockito.MockBean; |  | ||||||
| import org.springframework.context.annotation.Import; | import org.springframework.context.annotation.Import; | ||||||
| import org.springframework.test.annotation.Commit; |  | ||||||
| import org.springframework.test.annotation.DirtiesContext; | import org.springframework.test.annotation.DirtiesContext; | ||||||
| import org.springframework.transaction.support.TransactionTemplate; |  | ||||||
| 
 | 
 | ||||||
| import jakarta.persistence.EntityManager; |  | ||||||
| import jakarta.persistence.PersistenceContext; |  | ||||||
| import jakarta.servlet.http.HttpServletRequest; |  | ||||||
| import jakarta.validation.constraints.NotNull; |  | ||||||
| import java.io.*; | import java.io.*; | ||||||
| import java.math.BigDecimal; |  | ||||||
| import java.nio.file.Files; |  | ||||||
| import java.nio.file.Path; |  | ||||||
| import java.time.LocalDate; | import java.time.LocalDate; | ||||||
| import java.util.*; | import java.util.*; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import static java.lang.Boolean.parseBoolean; |  | ||||||
| import static java.util.Arrays.stream; | import static java.util.Arrays.stream; | ||||||
| import static java.util.Objects.requireNonNull; |  | ||||||
| import static java.util.Optional.ofNullable; | import static java.util.Optional.ofNullable; | ||||||
| import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; | import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; | ||||||
| import static org.apache.commons.lang3.StringUtils.isBlank; | import static org.apache.commons.lang3.StringUtils.isBlank; | ||||||
| @@ -96,9 +75,9 @@ import static org.assertj.core.api.Fail.fail; | |||||||
| 
 | 
 | ||||||
|  * To finally import the office data, run: |  * To finally import the office data, run: | ||||||
|  * |  * | ||||||
|  *   import-office-tables # comes from .aliases file and uses .environment |  *   gw-importOfficeTables # comes from .aliases file and uses .environment | ||||||
|  */ |  */ | ||||||
| @Tag("import") | @Tag("importOfficeData") | ||||||
| @DataJpaTest(properties = { | @DataJpaTest(properties = { | ||||||
|         "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", |         "spring.datasource.url=${HSADMINNG_POSTGRES_JDBC_URL:jdbc:tc:postgresql:15.5-bookworm:///spring_boot_testcontainers}", | ||||||
|         "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}", |         "spring.datasource.username=${HSADMINNG_POSTGRES_ADMIN_USERNAME:ADMIN}", | ||||||
| @@ -109,7 +88,7 @@ import static org.assertj.core.api.Fail.fail; | |||||||
| @Import({ Context.class, JpaAttempt.class }) | @Import({ Context.class, JpaAttempt.class }) | ||||||
| @TestMethodOrder(MethodOrderer.OrderAnnotation.class) | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) | ||||||
| @ExtendWith(OrderedDependedTestsExtension.class) | @ExtendWith(OrderedDependedTestsExtension.class) | ||||||
| public class ImportOfficeData extends ContextBasedTest { | public class ImportOfficeData extends CsvDataImport { | ||||||
| 
 | 
 | ||||||
|     private static final String[] SUBSCRIBER_ROLES = new String[] { |     private static final String[] SUBSCRIBER_ROLES = new String[] { | ||||||
|             "subscriber:operations-discussion", |             "subscriber:operations-discussion", | ||||||
| @@ -123,15 +102,16 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|             new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"}, |             new String[]{"partner", "vip-contact", "ex-partner", "billing", "contractual", "operation"}, | ||||||
|             SUBSCRIBER_ROLES); |             SUBSCRIBER_ROLES); | ||||||
| 
 | 
 | ||||||
|     // at least as the number of lines in business-partners.csv from test-data, but less than real data partner count |     // at least as the number of lines in business_partners.csv from test-data, but less than real data partner count | ||||||
|     public static final int MAX_NUMBER_OF_TEST_DATA_PARTNERS = 100; |     public static final int MAX_NUMBER_OF_TEST_DATA_PARTNERS = 100; | ||||||
|     public static final String MIGRATION_DATA_PATH = ofNullable(System.getenv("HSADMINNG_MIGRATION_DATA_PATH")).orElse("migration") + "/"; |  | ||||||
| 
 | 
 | ||||||
|     static int relationId = 2000000; |     static int relationId = 2000000; | ||||||
| 
 | 
 | ||||||
|     private static final List<Integer> IGNORE_BUSINESS_PARTNERS = Arrays.asList( |     private static final List<Integer> IGNORE_BUSINESS_PARTNERS = Arrays.asList( | ||||||
|         512167, // 11139, partner without contractual contact |         512167, // 11139, partner without contractual contact | ||||||
|         512170, // 11142, partner without contractual contact |         512170, // 11142, partner without contractual contact | ||||||
|  |         511725, // 10764, partner without contractual contact | ||||||
|  |         // 512171, // 11143, partner without partner contact -- exc | ||||||
|         -1 |         -1 | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
| @@ -140,44 +120,23 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         -1 |         -1 | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     @Value("${spring.datasource.url}") |     static Map<Integer, HsOfficeContactEntity> contacts = new WriteOnceMap<>(); | ||||||
|     private String jdbcUrl; |     static Map<Integer, HsOfficePersonEntity> persons = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, HsOfficePartnerEntity> partners = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>(); | ||||||
|  |     static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>(); | ||||||
| 
 | 
 | ||||||
|     @Value("${spring.datasource.username}") |     static Map<Integer, HsOfficeRelationEntity> relations = new WriteOnceMap<>(); | ||||||
|     private String postgresAdminUser; |     static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new WriteOnceMap<>(); | ||||||
| 
 |     static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new WriteOnceMap<>(); | ||||||
|     @Value("${hsadminng.superuser}") |     static Map<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new WriteOnceMap<>(); | ||||||
|     private String rbacSuperuser; |     static Map<Integer, HsOfficeCoopAssetsTransactionEntity> coopAssets = new WriteOnceMap<>(); | ||||||
| 
 |  | ||||||
|     private static Map<Integer, HsOfficeContactEntity> contacts = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficePersonEntity> persons = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficePartnerEntity> partners = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeDebitorEntity> debitors = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeMembershipEntity> memberships = new WriteOnceMap<>(); |  | ||||||
| 
 |  | ||||||
|     private static Map<Integer, HsOfficeRelationEntity> relations = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeSepaMandateEntity> sepaMandates = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeBankAccountEntity> bankAccounts = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeCoopSharesTransactionEntity> coopShares = new WriteOnceMap<>(); |  | ||||||
|     private static Map<Integer, HsOfficeCoopAssetsTransactionEntity> coopAssets = new WriteOnceMap<>(); |  | ||||||
| 
 |  | ||||||
|     @PersistenceContext |  | ||||||
|     EntityManager em; |  | ||||||
| 
 |  | ||||||
|     @Autowired |  | ||||||
|     TransactionTemplate txTemplate; |  | ||||||
| 
 |  | ||||||
|     @Autowired |  | ||||||
|     JpaAttempt jpaAttempt; |  | ||||||
| 
 |  | ||||||
|     @MockBean |  | ||||||
|     HttpServletRequest request; |  | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     @Order(1010) |     @Order(1010) | ||||||
|     void importBusinessPartners() { |     void importBusinessPartners() { | ||||||
| 
 | 
 | ||||||
|         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "business-partners.csv")) { |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/office/business_partners.csv")) { | ||||||
|             final var lines = readAllLines(reader); |             final var lines = readAllLines(reader); | ||||||
|             importBusinessPartners(justHeader(lines), withoutHeader(lines)); |             importBusinessPartners(justHeader(lines), withoutHeader(lines)); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
| @@ -193,28 +152,39 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         // no contacts yet => mostly null values |         // no contacts yet => mostly null values | ||||||
|         assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=partner(P-10017: null null, null), |                    100=partner(P-10003: null null, null), | ||||||
|                     20=partner(P-10020: null null, null), |                    120=partner(P-10020: null null, null), | ||||||
|                     22=partner(P-11022: null null, null), |                    122=partner(P-11022: null null, null), | ||||||
|                     90=partner(P-19090: null null, null), |                    132=partner(P-10152: null null, null), | ||||||
|                     99=partner(P-19999: null null, null) |                    190=partner(P-19090: null null, null), | ||||||
|  |                    199=partner(P-19999: null null, null), | ||||||
|  |                    213=partner(P-10000: null null, null), | ||||||
|  |                    541=partner(P-11018: null null, null), | ||||||
|  |                    542=partner(P-11019: null null, null) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(contacts)).isEqualTo("{}"); |         assertThat(toFormattedString(contacts)).isEqualTo("{}"); | ||||||
|         assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=debitor(D-1001700: rel(anchor='null null, null', type='DEBITOR'), mih), |                    100=debitor(D-1000300: rel(anchor='null null, null', type='DEBITOR'), mim), | ||||||
|                     20=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz), |                    120=debitor(D-1002000: rel(anchor='null null, null', type='DEBITOR'), xyz), | ||||||
|                     22=debitor(D-1102200: rel(anchor='null null, null', type='DEBITOR'), xxx), |                    122=debitor(D-1102200: rel(anchor='null null, null', type='DEBITOR'), xxx), | ||||||
|                     90=debitor(D-1909000: rel(anchor='null null, null', type='DEBITOR'), yyy), |                    132=debitor(D-1015200: rel(anchor='null null, null', type='DEBITOR'), rar), | ||||||
|                     99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz) |                    190=debitor(D-1909000: rel(anchor='null null, null', type='DEBITOR'), yyy), | ||||||
|  |                    199=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz), | ||||||
|  |                    213=debitor(D-1000000: rel(anchor='null null, null', type='DEBITOR'), hsh), | ||||||
|  |                    541=debitor(D-1101800: rel(anchor='null null, null', type='DEBITOR'), wws), | ||||||
|  |                    542=debitor(D-1101900: rel(anchor='null null, null', type='DEBITOR'), dph) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE), |                    100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE), | ||||||
|                     20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), |                    120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), | ||||||
|                     22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE) |                    122=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE), | ||||||
|  |                    132=Membership(M-1015200, P-10152, [2003-07-12,), ACTIVE), | ||||||
|  |                    541=Membership(M-1101800, P-11018, [2021-05-17,), ACTIVE), | ||||||
|  |                    542=Membership(M-1101900, P-11019, [2021-05-25,), ACTIVE) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|     } |     } | ||||||
| @@ -222,8 +192,7 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1020) |     @Order(1020) | ||||||
|     void importContacts() { |     void importContacts() { | ||||||
| 
 |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/office/contacts.csv")) { | ||||||
|         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "contacts.csv")) { |  | ||||||
|             final var lines = readAllLines(reader); |             final var lines = readAllLines(reader); | ||||||
|             importContacts(justHeader(lines), withoutHeader(lines)); |             importContacts(justHeader(lines), withoutHeader(lines)); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
| @@ -238,83 +207,151 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
| 
 | 
 | ||||||
|         assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(partners)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=partner(P-10017: NP Mellies, Michael, Herr Michael Mellies ), |                    100=partner(P-10003: ?? Michael Mellis, Herr Michael Mellis , Michael Mellis), | ||||||
|                     20=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH), |                    120=partner(P-10020: LP JM GmbH, Herr Philip Meyer-Contract , JM GmbH), | ||||||
|                     22=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS), |                    122=partner(P-11022: ?? Test PS, Petra Schmidt , Test PS), | ||||||
|                     90=partner(P-19090: NP Camus, Cecilia, Frau Cecilia Camus ), |                    132=partner(P-10152: ?? Ragnar IT-Beratung, Herr Ragnar Richter , Ragnar IT-Beratung), | ||||||
|                     99=partner(P-19999: null null, null) |                    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) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(contacts)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     1101=contact(caption='Herr Michael Mellies ', emailAddresses='{ "main": "mih@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"}'), |                    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"}'), |                    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"}'), |                    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"}'), |                    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"}'), |                    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"}'), |                    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"}'), |                    1401=contact(caption='Frau Frauke Fanninga ', emailAddresses='{ "main": "ff@example.org"}'), | ||||||
|                     1501=contact(caption='Frau Cecilia Camus ', emailAddresses='{ "main": "cc@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(toFormattedString(persons)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(persons)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     1=person(personType='LP', tradeName='Hostsharing eG'), |                    100=person(personType='??', tradeName='Michael Mellis', familyName='Mellis', givenName='Michael'), | ||||||
|                     1101=person(personType='NP', familyName='Mellies', givenName='Michael'), |  | ||||||
|                    1200=person(personType='LP', tradeName='JM e.K.'), |                    1200=person(personType='LP', tradeName='JM e.K.'), | ||||||
|                    1201=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Billing', givenName='Jenny'), |                    1201=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Billing', givenName='Jenny'), | ||||||
|                    1202=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Operation', givenName='Andrew'), |                    1202=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Operation', givenName='Andrew'), | ||||||
|                    1203=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'), |                    1203=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-Contract', givenName='Philip'), | ||||||
|                    1204=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-VIP', givenName='Tammy'), |                    1204=person(personType='LP', tradeName='JM GmbH', familyName='Meyer-VIP', givenName='Tammy'), | ||||||
|                    1301=person(personType='??', tradeName='Test PS', familyName='Schmidt', givenName='Petra'), |                    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'), |                    1401=person(personType='NP', familyName='Fanninga', givenName='Frauke'), | ||||||
|                     1501=person(personType='NP', familyName='Camus', givenName='Cecilia') |                    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.'), | ||||||
|  |                    90629=person(personType='NP', familyName='Richter', givenName='Ragnar'), | ||||||
|  |                    90677=person(personType='NP', familyName='Henning', givenName='Eike'), | ||||||
|  |                    90698=person(personType='NP', familyName='Henning', givenName='Jan') | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(debitors)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=debitor(D-1001700: rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael'), mih), |                    100=debitor(D-1000300: rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis'), mim), | ||||||
|                     20=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz), |                    120=debitor(D-1002000: rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH'), xyz), | ||||||
|                     22=debitor(D-1102200: rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS'), xxx), |                    122=debitor(D-1102200: rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS'), xxx), | ||||||
|                     90=debitor(D-1909000: rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia'), yyy), |                    132=debitor(D-1015200: rel(anchor='?? Ragnar IT-Beratung', type='DEBITOR', holder='?? Ragnar IT-Beratung'), rar), | ||||||
|                     99=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz) |                    190=debitor(D-1909000: rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia'), yyy), | ||||||
|  |                    199=debitor(D-1999900: rel(anchor='null null, null', type='DEBITOR'), zzz), | ||||||
|  |                    213=debitor(D-1000000: rel(anchor='LP Hostsharing e.G.', type='DEBITOR', holder='LP Hostsharing e.G.'), hsh), | ||||||
|  |                    541=debitor(D-1101800: rel(anchor='?? Wasserwerk Südholstein', type='DEBITOR', holder='?? Wasserwerk Südholstein'), wws), | ||||||
|  |                    542=debitor(D-1101900: rel(anchor='?? Das Perfekte Haus', type='DEBITOR', holder='?? Das Perfekte Haus'), dph) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE), |                    100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE), | ||||||
|                     20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), |                    120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), | ||||||
|                     22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE) |                    122=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE), | ||||||
|  |                    132=Membership(M-1015200, P-10152, [2003-07-12,), ACTIVE), | ||||||
|  |                    541=Membership(M-1101800, P-11018, [2021-05-17,), ACTIVE), | ||||||
|  |                    542=Membership(M-1101900, P-11019, [2021-05-25,), ACTIVE) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|         assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(relations)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     2000000=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), |                    2000000=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000001=rel(anchor='NP Mellies, Michael', type='DEBITOR', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), |                    2000001=rel(anchor='?? Michael Mellis', type='DEBITOR', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000002=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), |                    2000002=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'), | ||||||
|                     2000003=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), |                    2000003=rel(anchor='?? Ragnar IT-Beratung', type='DEBITOR', holder='?? Ragnar IT-Beratung', contact='Herr Ragnar Richter , Ragnar IT-Beratung'), | ||||||
|                     2000004=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'), |                    2000004=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'), | ||||||
|                     2000005=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'), |                    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 eG', type='PARTNER', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '), |                    2000006=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'), | ||||||
|                     2000007=rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '), |                    2000007=rel(anchor='?? Wasserwerk Südholstein', type='DEBITOR', holder='?? Wasserwerk Südholstein', contact='Frau Christiane Milberg , Wasserwerk Südholstein'), | ||||||
|                     2000008=rel(anchor='LP Hostsharing eG', type='PARTNER', holder='null null, null'), |                    2000008=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Das Perfekte Haus', contact='Herr Richard Wiese , Das Perfekte Haus'), | ||||||
|                     2000009=rel(anchor='null null, null', type='DEBITOR'), |                    2000009=rel(anchor='?? Das Perfekte Haus', type='DEBITOR', holder='?? Das Perfekte Haus', contact='Herr Inhaber R. Wiese , Das Perfekte Haus'), | ||||||
|                     2000010=rel(anchor='NP Mellies, Michael', type='OPERATIONS', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), |                    2000010=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), | ||||||
|                     2000011=rel(anchor='NP Mellies, Michael', type='REPRESENTATIVE', holder='NP Mellies, Michael', contact='Herr Michael Mellies '), |                    2000011=rel(anchor='LP JM GmbH', type='DEBITOR', holder='LP JM GmbH', contact='Frau Dr. Jenny Meyer-Billing , JM GmbH'), | ||||||
|                     2000012=rel(anchor='LP JM GmbH', type='EX_PARTNER', holder='LP JM e.K.', contact='JM e.K.'), |                    2000012=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='?? Test PS', contact='Petra Schmidt , Test PS'), | ||||||
|                     2000013=rel(anchor='LP JM GmbH', type='OPERATIONS', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), |                    2000013=rel(anchor='?? Test PS', type='DEBITOR', holder='?? Test PS', contact='Petra Schmidt , Test PS'), | ||||||
|                     2000014=rel(anchor='LP JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), |                    2000014=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '), | ||||||
|                     2000015=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='operations-announce', holder='LP JM GmbH', contact='Herr Andrew Meyer-Operation , JM GmbH'), |                    2000015=rel(anchor='NP Camus, Cecilia', type='DEBITOR', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '), | ||||||
|                     2000016=rel(anchor='LP JM GmbH', type='REPRESENTATIVE', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), |                    2000016=rel(anchor='LP Hostsharing e.G.', type='PARTNER', holder='null null, null'), | ||||||
|                     2000017=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='members-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), |                    2000017=rel(anchor='null null, null', type='DEBITOR'), | ||||||
|                     2000018=rel(anchor='LP JM GmbH', type='SUBSCRIBER', mark='customers-announce', holder='LP JM GmbH', contact='Herr Philip Meyer-Contract , JM GmbH'), |                    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 JM GmbH', type='VIP_CONTACT', holder='LP JM GmbH', contact='Frau Tammy Meyer-VIP , JM GmbH'), |                    2000019=rel(anchor='LP Hostsharing e.G.', type='REPRESENTATIVE', holder='LP Hostsharing e.G.', contact='Firma Hostmaster Hostsharing , Hostsharing e.G.'), | ||||||
|                     2000020=rel(anchor='?? Test PS', type='OPERATIONS', holder='?? Test PS', contact='Petra Schmidt , Test PS'), |                    2000020=rel(anchor='?? Michael Mellis', type='OPERATIONS', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000021=rel(anchor='?? Test PS', type='REPRESENTATIVE', holder='?? Test PS', contact='Petra Schmidt , Test PS'), |                    2000021=rel(anchor='?? Michael Mellis', type='REPRESENTATIVE', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000022=rel(anchor='NP Mellies, Michael', type='SUBSCRIBER', mark='operations-announce', holder='NP Fanninga, Frauke', contact='Frau Frauke Fanninga '), |                    2000022=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-discussion', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000023=rel(anchor='NP Camus, Cecilia', type='OPERATIONS', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus '), |                    2000023=rel(anchor='?? Michael Mellis', type='SUBSCRIBER', mark='operations-announce', holder='?? Michael Mellis', contact='Herr Michael Mellis , Michael Mellis'), | ||||||
|                     2000024=rel(anchor='NP Camus, Cecilia', type='REPRESENTATIVE', holder='NP Camus, Cecilia', contact='Frau Cecilia Camus ') |                    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 ') | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|     } |     } | ||||||
| @@ -322,8 +359,9 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1030) |     @Order(1030) | ||||||
|     void importSepaMandates() { |     void importSepaMandates() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
| 
 | 
 | ||||||
|         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "sepa-mandates.csv")) { |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/office/sepa_mandates.csv")) { | ||||||
|             final var lines = readAllLines(reader); |             final var lines = readAllLines(reader); | ||||||
|             importSepaMandates(justHeader(lines), withoutHeader(lines)); |             importSepaMandates(justHeader(lines), withoutHeader(lines)); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
| @@ -334,20 +372,29 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1039) |     @Order(1039) | ||||||
|     void verifySepaMandates() { |     void verifySepaMandates() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
|         assumeThatWeAreImportingControlledTestData(); |         assumeThatWeAreImportingControlledTestData(); | ||||||
| 
 | 
 | ||||||
|         assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(bankAccounts)).isEqualToIgnoringWhitespace(""" | ||||||
|             { |             { | ||||||
|                 234234=bankAccount(DE37500105177419788228: holder='Michael Mellies', bic='INGDDEFFXXX'), |                132=bankAccount(DE37500105177419788228: holder='Michael Mellis', bic='GENODEF1HH2'), | ||||||
|  |                234234=bankAccount(DE37500105177419788228: holder='Michael Mellis', bic='INGDDEFFXXX'), | ||||||
|                235600=bankAccount(DE02300209000106531065: holder='JM e.K.', bic='CMCIDEDD'), |                235600=bankAccount(DE02300209000106531065: holder='JM e.K.', bic='CMCIDEDD'), | ||||||
|                 235662=bankAccount(DE49500105174516484892: holder='JM GmbH', bic='INGDDEFFXXX') |                235662=bankAccount(DE49500105174516484892: holder='JM GmbH', bic='INGDDEFFXXX'), | ||||||
|  |                30=bankAccount(DE02300209000106531065: holder='Ragnar Richter', bic='GENODEM1GLS'), | ||||||
|  |                386=bankAccount(DE49500105174516484892: holder='Wasserwerk Suedholstein', bic='NOLADE21WHO'), | ||||||
|  |                387=bankAccount(DE89370400440532013000: holder='Richard Wiese Das Perfekte Haus', bic='COBADEFFXXX') | ||||||
|             } |             } | ||||||
|             """); |             """); | ||||||
|         assertThat(toFormattedString(sepaMandates)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(sepaMandates)).isEqualToIgnoringWhitespace(""" | ||||||
|             { |             { | ||||||
|  |                132=SEPA-Mandate(DE37500105177419788228, HS-10003-20140801, 2013-12-01, [2013-12-01,)), | ||||||
|                234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)), |                234234=SEPA-Mandate(DE37500105177419788228, MH12345, 2004-06-12, [2004-06-15,)), | ||||||
|                235600=SEPA-Mandate(DE02300209000106531065, JM33344, 2004-01-15, [2004-01-20,2005-06-28)), |                235600=SEPA-Mandate(DE02300209000106531065, JM33344, 2004-01-15, [2004-01-20,2005-06-28)), | ||||||
|                 235662=SEPA-Mandate(DE49500105174516484892, JM33344, 2005-06-28, [2005-07-01,)) |                235662=SEPA-Mandate(DE49500105174516484892, JM33344, 2005-06-28, [2005-07-01,)), | ||||||
|  |                30=SEPA-Mandate(DE02300209000106531065, HS-10152-20140801, 2013-12-01, [2013-12-01,2016-02-16)), | ||||||
|  |                386=SEPA-Mandate(DE49500105174516484892, HS-11018-20210512, 2021-05-12, [2021-05-17,)), | ||||||
|  |                387=SEPA-Mandate(DE89370400440532013000, HS-11019-20210519, 2021-05-19, [2021-05-25,)) | ||||||
|             } |             } | ||||||
|             """); |             """); | ||||||
|     } |     } | ||||||
| @@ -355,7 +402,9 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1040) |     @Order(1040) | ||||||
|     void importCoopShares() { |     void importCoopShares() { | ||||||
|         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "share-transactions.csv")) { |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
|  | 
 | ||||||
|  |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/office/share_transactions.csv")) { | ||||||
|             final var lines = readAllLines(reader); |             final var lines = readAllLines(reader); | ||||||
|             importCoopShares(justHeader(lines), withoutHeader(lines)); |             importCoopShares(justHeader(lines), withoutHeader(lines)); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
| @@ -366,14 +415,22 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1041) |     @Order(1041) | ||||||
|     void verifyCoopShares() { |     void verifyCoopShares() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
|         assumeThatWeAreImportingControlledTestData(); |         assumeThatWeAreImportingControlledTestData(); | ||||||
| 
 | 
 | ||||||
|         assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(coopShares)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     33443=CoopShareTransaction(M-1001700: 2000-12-06, SUBSCRIPTION, 20, 1001700, initial share subscription), |                    241=CoopShareTransaction(M-1000300: 2011-12-05, SUBSCRIPTION, 16, 1000300), | ||||||
|  |                    279=CoopShareTransaction(M-1015200: 2013-10-21, SUBSCRIPTION, 1, 1015200), | ||||||
|                    33451=CoopShareTransaction(M-1002000: 2000-12-06, SUBSCRIPTION, 2, 1002000, initial share subscription), |                    33451=CoopShareTransaction(M-1002000: 2000-12-06, SUBSCRIPTION, 2, 1002000, initial share subscription), | ||||||
|                     33701=CoopShareTransaction(M-1001700: 2005-01-10, SUBSCRIPTION, 40, 1001700, increase), |                    33701=CoopShareTransaction(M-1000300: 2005-01-10, SUBSCRIPTION, 40, 1000300, increase), | ||||||
|                     33810=CoopShareTransaction(M-1002000: 2016-12-31, CANCELLATION, 22, 1002000, membership ended) |                    33810=CoopShareTransaction(M-1002000: 2016-12-31, CANCELLATION, 22, 1002000, membership ended), | ||||||
|  |                    3=CoopShareTransaction(M-1000300: 2000-12-06, SUBSCRIPTION, 80, 1000300, initial share subscription), | ||||||
|  |                    523=CoopShareTransaction(M-1000300: 2020-12-08, SUBSCRIPTION, 96, 1000300, Kapitalerhoehung), | ||||||
|  |                    562=CoopShareTransaction(M-1101800: 2021-05-17, SUBSCRIPTION, 4, 1101800, Beitritt), | ||||||
|  |                    563=CoopShareTransaction(M-1101900: 2021-05-25, SUBSCRIPTION, 1, 1101900, Beitritt), | ||||||
|  |                    721=CoopShareTransaction(M-1000300: 2023-10-10, SUBSCRIPTION, 96, 1000300, Kapitalerhoehung), | ||||||
|  |                    90=CoopShareTransaction(M-1015200: 2003-07-12, SUBSCRIPTION, 1, 1015200) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|     } |     } | ||||||
| @@ -381,8 +438,9 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1050) |     @Order(1050) | ||||||
|     void importCoopAssets() { |     void importCoopAssets() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
| 
 | 
 | ||||||
|         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "asset-transactions.csv")) { |         try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "/office/asset_transactions.csv")) { | ||||||
|             final var lines = readAllLines(reader); |             final var lines = readAllLines(reader); | ||||||
|             importCoopAssets(justHeader(lines), withoutHeader(lines)); |             importCoopAssets(justHeader(lines), withoutHeader(lines)); | ||||||
|         } catch (Exception e) { |         } catch (Exception e) { | ||||||
| @@ -393,20 +451,29 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1059) |     @Order(1059) | ||||||
|     void verifyCoopAssets() { |     void verifyCoopAssets() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
|         assumeThatWeAreImportingControlledTestData(); |         assumeThatWeAreImportingControlledTestData(); | ||||||
| 
 | 
 | ||||||
|         assertThat(toFormattedString(coopAssets)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(coopAssets)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     30000=CoopAssetsTransaction(M-1001700: 2000-12-06, DEPOSIT, 1280.00, 1001700, for subscription A), |                    1093=CoopAssetsTransaction(M-1000300: 2023-10-05, DEPOSIT, 3072, 1000300, Kapitalerhoehung - Ueberweisung), | ||||||
|  |                    1094=CoopAssetsTransaction(M-1000300: 2023-10-06, DEPOSIT, 3072, 1000300, Kapitalerhoehung - Ueberweisung), | ||||||
|                    31000=CoopAssetsTransaction(M-1002000: 2000-12-06, DEPOSIT, 128.00, 1002000, for subscription B), |                    31000=CoopAssetsTransaction(M-1002000: 2000-12-06, DEPOSIT, 128.00, 1002000, for subscription B), | ||||||
|                     32000=CoopAssetsTransaction(M-1001700: 2005-01-10, DEPOSIT, 2560.00, 1001700, for subscription C), |                    32000=CoopAssetsTransaction(M-1000300: 2005-01-10, DEPOSIT, 2560.00, 1000300, for subscription C), | ||||||
|                     33001=CoopAssetsTransaction(M-1001700: 2005-01-10, TRANSFER, -512.00, 1001700, for transfer to 10), |                    33001=CoopAssetsTransaction(M-1000300: 2005-01-10, TRANSFER, -512.00, 1000300, for transfer to 10), | ||||||
|                    33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, 1002000, for transfer from 7), |                    33002=CoopAssetsTransaction(M-1002000: 2005-01-10, ADOPTION, 512.00, 1002000, for transfer from 7), | ||||||
|                    34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, 1002000, for cancellation D), |                    34001=CoopAssetsTransaction(M-1002000: 2016-12-31, CLEARING, -8.00, 1002000, for cancellation D), | ||||||
|                    34002=CoopAssetsTransaction(M-1002000: 2016-12-31, DISBURSAL, -100.00, 1002000, for cancellation D), |                    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), |                    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), |                    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, ADJUSTMENT, -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), | ||||||
|  |                    632=CoopAssetsTransaction(M-1015200: 2013-10-21, DEPOSIT, 64, 1015200), | ||||||
|  |                    885=CoopAssetsTransaction(M-1000300: 2020-12-15, DEPOSIT, 6144, 1000300, Einzahlung), | ||||||
|  |                    924=CoopAssetsTransaction(M-1101800: 2021-05-21, DEPOSIT, 256, 1101800, Beitritt - Lastschrift), | ||||||
|  |                    925=CoopAssetsTransaction(M-1101900: 2021-05-31, DEPOSIT, 64, 1101900, Beitritt - Lastschrift) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|     } |     } | ||||||
| @@ -414,13 +481,18 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     @Test |     @Test | ||||||
|     @Order(1099) |     @Order(1099) | ||||||
|     void verifyMemberships() { |     void verifyMemberships() { | ||||||
|  |         assumeThatWeAreExplicitlyImportingOfficeData(); | ||||||
|         assumeThatWeAreImportingControlledTestData(); |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  | 
 | ||||||
|         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" |         assertThat(toFormattedString(memberships)).isEqualToIgnoringWhitespace(""" | ||||||
|                 { |                 { | ||||||
|                     17=Membership(M-1001700, P-10017, [2000-12-06,), ACTIVE), |                    100=Membership(M-1000300, P-10003, [2000-12-06,), ACTIVE), | ||||||
|                     20=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), |                    120=Membership(M-1002000, P-10020, [2000-12-06,2016-01-01), UNKNOWN), | ||||||
|                     22=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE), |                    122=Membership(M-1102200, P-11022, [2021-04-01,), ACTIVE), | ||||||
|                     90=Membership(M-1909000, P-19090, empty, INVALID) |                    132=Membership(M-1015200, P-10152, [2003-07-12,), ACTIVE), | ||||||
|  |                    190=Membership(M-1909000, P-19090, empty, INVALID), | ||||||
|  |                    541=Membership(M-1101800, P-11018, [2021-05-17,), ACTIVE), | ||||||
|  |                    542=Membership(M-1101900, P-11019, [2021-05-25,), ACTIVE) | ||||||
|                 } |                 } | ||||||
|                 """); |                 """); | ||||||
|     } |     } | ||||||
| @@ -431,11 +503,11 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         partners.forEach((id, p) -> { |         partners.forEach((id, p) -> { | ||||||
|             final var partnerRel = p.getPartnerRel(); |             final var partnerRel = p.getPartnerRel(); | ||||||
|             assertThat(partnerRel).describedAs("partner " + id + " without partnerRel").isNotNull(); |             assertThat(partnerRel).describedAs("partner " + id + " without partnerRel").isNotNull(); | ||||||
|             if ( id != 99 ) { |             if ( id != 199 ) { | ||||||
|                 assertThat(partnerRel.getContact()).describedAs("partner " + id + " without partnerRel.contact").isNotNull(); |                 logError( () -> assertThat(partnerRel.getContact()).describedAs("partner " + id + " without partnerRel.contact").isNotNull()); | ||||||
|                 assertThat(partnerRel.getContact().getCaption()).describedAs("partner " + id + " without valid partnerRel.contact").isNotNull(); |                 logError( () -> assertThat(partnerRel.getContact().getCaption()).describedAs("partner " + id + " without valid partnerRel.contact").isNotNull()); | ||||||
|                 assertThat(partnerRel.getHolder()).describedAs("partner " + id + " without partnerRel.relHolder").isNotNull(); |                 logError( () -> assertThat(partnerRel.getHolder()).describedAs("partner " + id + " without partnerRel.relHolder").isNotNull()); | ||||||
|                 assertThat(partnerRel.getHolder().getPersonType()).describedAs("partner " + id + " without valid partnerRel.relHolder").isNotNull(); |                 logError( () -> assertThat(partnerRel.getHolder().getPersonType()).describedAs("partner " + id + " without valid partnerRel.relHolder").isNotNull()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @@ -523,13 +595,29 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|     @Order(9000) |     @Order(3005) | ||||||
|     @Commit |     void removeEmptyPersons() { | ||||||
|     void persistEntities() { |         // avoid a error when persisting the deliberately invalid partner entry #99 | ||||||
|  |         final var idsToRemove = new HashSet<Integer>(); | ||||||
|  |         persons.forEach( (id, p) -> { | ||||||
|  |             if ( p.getPersonType() == null || | ||||||
|  |                 (p.getFamilyName() == null && p.getGivenName() == null && p.getTradeName() == null) ) { | ||||||
|  |                 idsToRemove.add(id); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         idsToRemove.forEach(id -> persons.remove(id)); | ||||||
| 
 | 
 | ||||||
|         System.out.println("PERSISTING to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); |         assumeThatWeAreImportingControlledTestData(); | ||||||
|  |         assertThat(idsToRemove.size()).isEqualTo(0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     @Order(9000) | ||||||
|  |     void persistOfficeEntities() { | ||||||
|  | 
 | ||||||
|  |         System.out.println("PERSISTING office data to database '" + jdbcUrl + "' as user '" + postgresAdminUser + "'"); | ||||||
|         deleteTestDataFromHsOfficeTables(); |         deleteTestDataFromHsOfficeTables(); | ||||||
|         resetFromHsOfficeSequences(); |         resetHsOfficeSequences(); | ||||||
|         deleteFromTestTables(); |         deleteFromTestTables(); | ||||||
|         deleteFromRbacTables(); |         deleteFromRbacTables(); | ||||||
| 
 | 
 | ||||||
| @@ -542,6 +630,8 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         jpaAttempt.transacted(() -> { |         jpaAttempt.transacted(() -> { | ||||||
|             context(rbacSuperuser); |             context(rbacSuperuser); | ||||||
|             persons.forEach(this::persist); |             persons.forEach(this::persist); | ||||||
|  |             relations.forEach( (id, rel) -> this.persist(id, rel.getAnchor()) ); | ||||||
|  |             relations.forEach( (id, rel) -> this.persist(id, rel.getHolder()) ); | ||||||
|         }).assertSuccessful(); |         }).assertSuccessful(); | ||||||
| 
 | 
 | ||||||
|         jpaAttempt.transacted(() -> { |         jpaAttempt.transacted(() -> { | ||||||
| @@ -602,18 +692,8 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void persist(final Integer id, final RbacObject entity) { |     protected void assumeThatWeAreExplicitlyImportingOfficeData() { | ||||||
|         try { |         // not throwing AssumptionException | ||||||
|             //System.out.println("persisting #" + entity.hashCode() + ": " + entity); |  | ||||||
|             em.persist(entity); |  | ||||||
|             // uncomment for debugging purposes |  | ||||||
|             // em.flush(); |  | ||||||
|             // System.out.println("persisted #" + entity.hashCode() + " as " + entity.getUuid()); |  | ||||||
|         } catch (Exception exc) { |  | ||||||
|             System.err.println("failed to persist #" + entity.hashCode() + ": " + entity); |  | ||||||
|             System.err.println(exc); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static boolean isImportingControlledTestData() { |     private static boolean isImportingControlledTestData() { | ||||||
| @@ -624,62 +704,6 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         assumeThat(partners.size()).isLessThanOrEqualTo(MAX_NUMBER_OF_TEST_DATA_PARTNERS); |         assumeThat(partners.size()).isLessThanOrEqualTo(MAX_NUMBER_OF_TEST_DATA_PARTNERS); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void deleteTestDataFromHsOfficeTables() { |  | ||||||
|         jpaAttempt.transacted(() -> { |  | ||||||
|             context(rbacSuperuser); |  | ||||||
|             em.createNativeQuery("delete from hs_hosting_asset where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_booking_item where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_booking_project where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_coopassetstransaction where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_coopassetstransaction_legacy_id where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_coopsharestransaction where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_coopsharestransaction_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(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_debitor where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_bankaccount where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_partner where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_partner_details where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_relation where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_contact where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from hs_office_person where true").executeUpdate(); |  | ||||||
|         }).assertSuccessful(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void resetFromHsOfficeSequences() { |  | ||||||
|         jpaAttempt.transacted(() -> { |  | ||||||
|             context(rbacSuperuser); |  | ||||||
|             em.createNativeQuery("alter sequence hs_office_contact_legacy_id_seq restart with 1000000000;").executeUpdate(); |  | ||||||
|             em.createNativeQuery("alter sequence hs_office_coopassetstransaction_legacy_id_seq restart with 1000000000;") |  | ||||||
|                     .executeUpdate(); |  | ||||||
|             em.createNativeQuery("alter sequence public.hs_office_coopsharestransaction_legacy_id_seq restart with 1000000000;") |  | ||||||
|                     .executeUpdate(); |  | ||||||
|             em.createNativeQuery("alter sequence public.hs_office_partner_legacy_id_seq restart with 1000000000;") |  | ||||||
|                     .executeUpdate(); |  | ||||||
|             em.createNativeQuery("alter sequence public.hs_office_sepamandate_legacy_id_seq restart with 1000000000;") |  | ||||||
|                     .executeUpdate(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void deleteFromTestTables() { |  | ||||||
|         jpaAttempt.transacted(() -> { |  | ||||||
|             context(rbacSuperuser); |  | ||||||
|             em.createNativeQuery("delete from test_domain where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from test_package where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from test_customer where true").executeUpdate(); |  | ||||||
|         }).assertSuccessful(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void deleteFromRbacTables() { |  | ||||||
|         jpaAttempt.transacted(() -> { |  | ||||||
|             context(rbacSuperuser); |  | ||||||
|             em.createNativeQuery("delete from rbacuser_rv where name not like 'superuser-%'").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from tx_journal where true").executeUpdate(); |  | ||||||
|             em.createNativeQuery("delete from tx_context where true").executeUpdate(); |  | ||||||
|         }).assertSuccessful(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private <E extends RbacObject> void updateLegacyIds( |     private <E extends RbacObject> void updateLegacyIds( | ||||||
|             Map<Integer, E> entities, |             Map<Integer, E> entities, | ||||||
|             final String legacyIdTable, |             final String legacyIdTable, | ||||||
| @@ -698,59 +722,25 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public List<String[]> readAllLines(Reader reader) throws Exception { |  | ||||||
| 
 |  | ||||||
|         final var parser = new CSVParserBuilder() |  | ||||||
|                 .withSeparator(';') |  | ||||||
|                 .withQuoteChar('"') |  | ||||||
|                 .build(); |  | ||||||
| 
 |  | ||||||
|         final var filteredReader = skippingEmptyAndCommentLines(reader); |  | ||||||
|         try (CSVReader csvReader = new CSVReaderBuilder(filteredReader) |  | ||||||
|                 .withCSVParser(parser) |  | ||||||
|                 .build()) { |  | ||||||
|             return csvReader.readAll(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public static Reader skippingEmptyAndCommentLines(Reader reader) throws IOException { |  | ||||||
|         try (var bufferedReader = new BufferedReader(reader); |  | ||||||
|              StringWriter writer = new StringWriter()) { |  | ||||||
| 
 |  | ||||||
|             String line; |  | ||||||
|             while ((line = bufferedReader.readLine()) != null) { |  | ||||||
|                 if (!line.isBlank() && !line.startsWith("#")) { |  | ||||||
|                     writer.write(line); |  | ||||||
|                     writer.write("\n"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return new StringReader(writer.toString()); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private void importBusinessPartners(final String[] header, final List<String[]> records) { |     private void importBusinessPartners(final String[] header, final List<String[]> records) { | ||||||
| 
 | 
 | ||||||
|         final var columns = new Columns(header); |         final var columns = new Columns(header); | ||||||
| 
 | 
 | ||||||
|         final var mandant = HsOfficePersonEntity.builder() |  | ||||||
|                 .personType(HsOfficePersonType.LEGAL_PERSON) |  | ||||||
|                 .tradeName("Hostsharing eG") |  | ||||||
|                 .build(); |  | ||||||
|         persons.put(1, mandant); |  | ||||||
| 
 |  | ||||||
|         records.stream() |         records.stream() | ||||||
|                 .map(this::trimAll) |                 .map(this::trimAll) | ||||||
|                 .map(row -> new Record(columns, row)) |                 .map(row -> new Record(columns, row)) | ||||||
|                 .forEach(rec -> { |                 .forEach(rec -> { | ||||||
|                     if (this.IGNORE_BUSINESS_PARTNERS.contains(rec.getInteger("bp_id"))) { |                     final Integer bpId = rec.getInteger("bp_id"); | ||||||
|  |                     if (IGNORE_BUSINESS_PARTNERS.contains(bpId)) { | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     final var person = HsOfficePersonEntity.builder().build(); |                     final var person = HsOfficePersonEntity.builder().build(); | ||||||
| 
 | 
 | ||||||
|                     final var partnerRel = addRelation( |                     final var partnerRel = addRelation( | ||||||
|                             HsOfficeRelationType.PARTNER, mandant, person, |                             HsOfficeRelationType.PARTNER, | ||||||
|  |                             null, // is set after contacts when the person for 'Hostsharing eG' is known | ||||||
|  |                             person, | ||||||
|                             null  // is set during contacts import depending on assigned roles |                             null  // is set during contacts import depending on assigned roles | ||||||
|                     ); |                     ); | ||||||
| 
 | 
 | ||||||
| @@ -759,7 +749,7 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                             .details(HsOfficePartnerDetailsEntity.builder().build()) |                             .details(HsOfficePartnerDetailsEntity.builder().build()) | ||||||
|                             .partnerRel(partnerRel) |                             .partnerRel(partnerRel) | ||||||
|                             .build(); |                             .build(); | ||||||
|                     partners.put(rec.getInteger("bp_id"), partner); |                     partners.put(bpId, partner); | ||||||
| 
 | 
 | ||||||
|                     final var debitorRel = addRelation( |                     final var debitorRel = addRelation( | ||||||
|                             HsOfficeRelationType.DEBITOR, partnerRel.getHolder(), // partner person |                             HsOfficeRelationType.DEBITOR, partnerRel.getHolder(), // partner person | ||||||
| @@ -777,7 +767,7 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                             .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove |                             .vatBusiness("GROSS".equals(rec.getString("indicator_vat"))) // TODO: remove | ||||||
|                             .vatId(rec.getString("uid_vat")) |                             .vatId(rec.getString("uid_vat")) | ||||||
|                             .build(); |                             .build(); | ||||||
|                     debitors.put(rec.getInteger("bp_id"), debitor); |                     debitors.put(bpId, debitor); | ||||||
| 
 | 
 | ||||||
|                     if (isNotBlank(rec.getString("member_since"))) { |                     if (isNotBlank(rec.getString("member_since"))) { | ||||||
|                         assertThat(rec.getInteger("member_id")).isEqualTo(partner.getPartnerNumber()); |                         assertThat(rec.getInteger("member_id")).isEqualTo(partner.getPartnerNumber()); | ||||||
| @@ -793,7 +783,7 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                                                 ? HsOfficeMembershipStatus.ACTIVE |                                                 ? HsOfficeMembershipStatus.ACTIVE | ||||||
|                                                 : HsOfficeMembershipStatus.UNKNOWN) |                                                 : HsOfficeMembershipStatus.UNKNOWN) | ||||||
|                                 .build(); |                                 .build(); | ||||||
|                         memberships.put(rec.getInteger("bp_id"), membership); |                         memberships.put(bpId, membership); | ||||||
|                     } |                     } | ||||||
|                 }); |                 }); | ||||||
|     } |     } | ||||||
| @@ -807,6 +797,10 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                 .map(row -> new Record(columns, row)) |                 .map(row -> new Record(columns, row)) | ||||||
|                 .forEach(rec -> { |                 .forEach(rec -> { | ||||||
|                     final var bpId = rec.getInteger("bp_id"); |                     final var bpId = rec.getInteger("bp_id"); | ||||||
|  |                     if (IGNORE_BUSINESS_PARTNERS.contains(bpId)) { | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|                     final var member = ofNullable(memberships.get(bpId)) |                     final var member = ofNullable(memberships.get(bpId)) | ||||||
|                             .orElseGet(() -> createOnDemandMembership(bpId)); |                             .orElseGet(() -> createOnDemandMembership(bpId)); | ||||||
| 
 | 
 | ||||||
| @@ -958,10 +952,10 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                     final var contactId = rec.getInteger("contact_id"); |                     final var contactId = rec.getInteger("contact_id"); | ||||||
|                     final var bpId = rec.getInteger("bp_id"); |                     final var bpId = rec.getInteger("bp_id"); | ||||||
| 
 | 
 | ||||||
|                     if (this.IGNORE_CONTACTS.contains(contactId)) { |                     if (IGNORE_CONTACTS.contains(contactId)) { | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|                     if (this.IGNORE_BUSINESS_PARTNERS.contains(bpId)) { |                     if (IGNORE_BUSINESS_PARTNERS.contains(bpId)) { | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
| @@ -1019,6 +1013,7 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|         assertNoMissingContractualRelations(); |         assertNoMissingContractualRelations(); | ||||||
|  |         useHostsharingAsPartnerAnchor(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static void assertNoMissingContractualRelations() { |     private static void assertNoMissingContractualRelations() { | ||||||
| @@ -1038,6 +1033,16 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private static void useHostsharingAsPartnerAnchor() { | ||||||
|  |         final var mandant = persons.values().stream() | ||||||
|  |                 .filter(p -> p.getTradeName().startsWith("Hostsharing e")) | ||||||
|  |                 .findFirst() | ||||||
|  |                 .orElseThrow(); | ||||||
|  |         relations.values().stream() | ||||||
|  |                 .filter(r -> r.getType() == HsOfficeRelationType.PARTNER) | ||||||
|  |                 .forEach(r -> r.setAnchor(mandant)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private static boolean containsRole(final Record rec, final String role) { |     private static boolean containsRole(final Record rec, final String role) { | ||||||
|         final var roles = rec.getString("roles"); |         final var roles = rec.getString("roles"); | ||||||
|         return ("," + roles + ",").contains("," + role + ","); |         return ("," + roles + ",").contains("," + role + ","); | ||||||
| @@ -1128,27 +1133,6 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|         return contact; |         return contact; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private <E> String toFormattedString(final Map<Integer, E> map) { |  | ||||||
|         if ( map.isEmpty() ) { |  | ||||||
|             return "{}"; |  | ||||||
|         } |  | ||||||
|         return "{\n" + |  | ||||||
|                 map.keySet().stream() |  | ||||||
|                         .map(id -> "   " + id + "=" + map.get(id).toString()) |  | ||||||
|                         .map(e -> e.replaceAll("\n    ", " ").replace("\n", "")) |  | ||||||
|                         .collect(Collectors.joining(",\n")) + |  | ||||||
|                 "\n}\n"; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private String[] trimAll(final String[] record) { |  | ||||||
|         for (int i = 0; i < record.length; ++i) { |  | ||||||
|             if (record[i] != null) { |  | ||||||
|                 record[i] = record[i].trim(); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return record; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private Map<String, String> toPhoneNumbers(final Record rec) { |     private Map<String, String> toPhoneNumbers(final Record rec) { | ||||||
|         final var phoneNumbers = new LinkedHashMap<String, String>(); |         final var phoneNumbers = new LinkedHashMap<String, String>(); | ||||||
|         if (isNotBlank(rec.getString("phone_private"))) |         if (isNotBlank(rec.getString("phone_private"))) | ||||||
| @@ -1218,104 +1202,4 @@ public class ImportOfficeData extends ContextBasedTest { | |||||||
|     private String toName(final String salut, final String title, final String firstname, final String lastname) { |     private String toName(final String salut, final String title, final String firstname, final String lastname) { | ||||||
|         return toCaption(salut, title, firstname, lastname, null); |         return toCaption(salut, title, firstname, lastname, null); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     private Reader resourceReader(@NotNull final String resourcePath) { |  | ||||||
|         return new InputStreamReader(requireNonNull(getClass().getClassLoader().getResourceAsStream(resourcePath))); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static String[] justHeader(final List<String[]> lines) { |  | ||||||
|         return stream(lines.getFirst()).map(String::trim).toArray(String[]::new); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private List<String[]> withoutHeader(final List<String[]> records) { |  | ||||||
|         return records.subList(1, records.size()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class Columns { |  | ||||||
| 
 |  | ||||||
|     private final List<String> columnNames; |  | ||||||
| 
 |  | ||||||
|     public Columns(final String[] header) { |  | ||||||
|         columnNames = List.of(header); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int indexOf(final String columnName) { |  | ||||||
|         int index = columnNames.indexOf(columnName); |  | ||||||
|         if (index < 0) { |  | ||||||
|             throw new RuntimeException("column name '" + columnName + "' not found in: " + columnNames); |  | ||||||
|         } |  | ||||||
|         return index; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class Record { |  | ||||||
| 
 |  | ||||||
|     private final Columns columns; |  | ||||||
|     private final String[] row; |  | ||||||
| 
 |  | ||||||
|     public Record(final Columns columns, final String[] row) { |  | ||||||
|         this.columns = columns; |  | ||||||
|         this.row = row; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     String getString(final String columnName) { |  | ||||||
|         return row[columns.indexOf(columnName)]; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     boolean isEmpty(final String columnName) { |  | ||||||
|         final String value = getString(columnName); |  | ||||||
|         return value == null || value.isBlank(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     boolean getBoolean(final String columnName) { |  | ||||||
|         final String value = getString(columnName); |  | ||||||
|         return isNotBlank(value) && |  | ||||||
|                 ( parseBoolean(value.trim()) || value.trim().startsWith("t")); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Integer getInteger(final String columnName) { |  | ||||||
|         final String value = getString(columnName); |  | ||||||
|         return isNotBlank(value) ? Integer.parseInt(value.trim()) : 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     BigDecimal getBigDecimal(final String columnName) { |  | ||||||
|         final String value = getString(columnName); |  | ||||||
|         if (isNotBlank(value)) { |  | ||||||
|             return new BigDecimal(value); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     LocalDate getLocalDate(final String columnName) { |  | ||||||
|         final String dateString = getString(columnName); |  | ||||||
|         if (isNotBlank(dateString)) { |  | ||||||
|             return LocalDate.parse(dateString); |  | ||||||
|         } |  | ||||||
|         return null; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class OrderedDependedTestsExtension implements TestWatcher, BeforeEachCallback { |  | ||||||
| 
 |  | ||||||
|     private static boolean previousTestsPassed = true; |  | ||||||
| 
 |  | ||||||
|     public void testFailed(ExtensionContext context, Throwable cause) { |  | ||||||
|         previousTestsPassed = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public void beforeEach(final ExtensionContext extensionContext) { |  | ||||||
|         assumeThat(previousTestsPassed).isTrue(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| class WriteOnceMap<K, V> extends TreeMap<K, V> { |  | ||||||
| 
 |  | ||||||
|     @Override |  | ||||||
|     public V put(final K k, final V v) { |  | ||||||
|         assertThat(containsKey(k)).describedAs("overwriting " + get(k) + " index " + k + " with " + v).isFalse(); |  | ||||||
|         return super.put(k, v); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| member_asset_id;	bp_id;	date;		action;		amount;	comment |  | ||||||
| 30000;				  17;	2000-12-06;	PAYMENT;	1280.00; for subscription A |  | ||||||
| 31000;				 20;	2000-12-06;	PAYMENT;	128.00; for subscription B |  | ||||||
| 32000;				 17;	2005-01-10;	PAYMENT;   2560.00; for subscription C |  | ||||||
| 33001;				 17;	2005-01-10;	HANDOVER;  -512.00; for transfer to 10 |  | ||||||
| 33002;				 20;	2005-01-10;	ADOPTION;	512.00; for transfer from 7 |  | ||||||
| 34001;				 20;	2016-12-31;	CLEARING;	 -8.00; for cancellation D |  | ||||||
| 34002;				 20;	2016-12-31;	PAYBACK;   -100.00; for cancellation D |  | ||||||
| 34003;				 20;	2016-12-31;	LOSS;		-20.00; for cancellation D |  | ||||||
| 35001;				 90;	2024-01-15;	PAYMENT;	128.00; for subscription E |  | ||||||
| 35002;				 90;	2024-01-20;	ADJUSTMENT;-128.00; chargeback for subscription E |  | ||||||
| 
 | 
| @@ -1,6 +0,0 @@ | |||||||
| bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat |  | ||||||
| 17;10017;hsh00-mih;2000-12-06;;Aufsichtsrat;2006-10-15;2001-10-15;false;false;NET;DE-VAT-007 |  | ||||||
| 20;10020;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; |  | ||||||
| 22;11022;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; |  | ||||||
| 90;19090;hsh00-yyy;;;;;;true;true;GROSS; |  | ||||||
| 99;19999;hsh00-zzz;;;;;;false;false;GROSS; |  | ||||||
| 
 | 
| @@ -1,20 +0,0 @@ | |||||||
| contact_id;	bp_id;	salut;	first_name;	last_name;	title;	firma;	co;	street;			zipcode;city;	country;	phone_private;		phone_office;		phone_mobile;	fax;		email; roles |  | ||||||
|  |  | ||||||
| # eine natürliche Person, implizites contractual |  | ||||||
| 1101;		17;	Herr;	Michael;	Mellies;		;	;	;	Kleine Freiheit 50;	26524;	Hage;	DE;		;			+49 4931 123456;	+49 1522 123456;;		mih@example.org; partner,contractual,billing,operation |  | ||||||
|  |  | ||||||
| # eine juristische Person mit drei separaten Ansprechpartnern, vip-contact und ex-partner |  | ||||||
| 1200;		20;;	;		;		;	JM e.K.;;	Wiesenweg 15;		12335;	Berlin; DE;		+49 30 6666666;		+49 30 5555555;		;		+49 30 6666666; jm-ex-partner@example.org; ex-partner |  | ||||||
| 1201;		20;	Frau;	Jenny;		Meyer-Billing;		Dr.;	JM GmbH;;	Waldweg 5;		11001;	Berlin; DE;		+49 30 7777777;		+49 30 1111111;		;		+49 30 2222222; jm-billing@example.org; billing |  | ||||||
| 1202;		20;	Herr;	Andrew;		Meyer-Operation;		;	JM GmbH;;	Waldweg 5;		11001;	Berlin; DE;		+49 30 6666666;		+49 30 3333333;		;		+49 30 4444444; am-operation@example.org; operation,vip-contact,subscriber:operations-announce |  | ||||||
| 1203;		20;	Herr;	Philip;		Meyer-Contract;		;	JM GmbH;;	Waldweg 5;		11001;	Berlin; DE;		+49 30 6666666;		+49 30 5555555;		;		+49 30 6666666; pm-partner@example.org; partner,contractual,subscriber:members-announce,subscriber:customers-announce |  | ||||||
| 1204;		20;	Frau;	Tammy;		Meyer-VIP;		;	JM GmbH;;	Waldweg 5;		11001;	Berlin; DE;		+49 30 999999;		+49 30 999999;		;		+49 30 6666666; tm-vip@example.org; vip-contact |  | ||||||
|  |  | ||||||
| # eine juristische Person mit nur einem Ansprechpartner und explizitem contractual |  | ||||||
| 1301;		22;	;	Petra;		Schmidt;	;	Test PS;;	;			;	;	;		;			;			;		;		ps@example.com; partner,billing,contractual,operation |  | ||||||
|  |  | ||||||
| # eine natürliche Person, die nur Subscriber ist |  | ||||||
| 1401;		17;	Frau;	Frauke;	Fanninga;		;	;	;	Am Walde 1;	29456;	Hitzacker;	DE;		;			;	;;		ff@example.org; subscriber:operations-announce |  | ||||||
|  |  | ||||||
| # eine natürliche Person als Partner |  | ||||||
| 1501;		90;	Frau;	Cecilia;	Camus;		;	;	;	Rue d'Avignion 60;	45000;	Orléans;	FR;		;			;	;;		cc@example.org; partner,contractual,billing,operation |  | ||||||
| 
 | 
| @@ -20,58 +20,58 @@ dump() { | |||||||
| dump "select bp_id, member_id, member_code, member_since, member_until, member_role, author_contract, nondisc_contract, free, exempt_vat, indicator_vat, uid_vat | dump "select bp_id, member_id, member_code, member_since, member_until, member_role, author_contract, nondisc_contract, free, exempt_vat, indicator_vat, uid_vat | ||||||
|       from business_partner |       from business_partner | ||||||
|       order by bp_id" \ |       order by bp_id" \ | ||||||
|      "business-partners.csv"  |      "office/business_partners.csv" | ||||||
|      |      | ||||||
| dump "select contact_id, bp_id, salut, first_name, last_name, title, firma, co, street, zipcode, city, country, phone_private, phone_office, phone_mobile, fax, email, array_to_string(array_agg(role), ',') as roles | dump "select contact_id, bp_id, salut, first_name, last_name, title, firma, co, street, zipcode, city, country, phone_private, phone_office, phone_mobile, fax, email, array_to_string(array_agg(role), ',') as roles | ||||||
|       from contact |       from contact | ||||||
|       left join contactrole_ref using(contact_id) |       left join contactrole_ref using(contact_id) | ||||||
|       group by contact_id |       group by contact_id | ||||||
|       order by contact_id" \ |       order by contact_id" \ | ||||||
|      "contacts.csv" |      "office/contacts.csv" | ||||||
|  |  | ||||||
| dump "select sepa_mandat_id, bp_id, bank_customer, bank_name, bank_iban, bank_bic, mandat_ref, mandat_signed, mandat_since, mandat_until, mandat_used | dump "select sepa_mandat_id, bp_id, bank_customer, bank_name, bank_iban, bank_bic, mandat_ref, mandat_signed, mandat_since, mandat_until, mandat_used | ||||||
|       from sepa_mandat |       from sepa_mandat | ||||||
|       order by sepa_mandat_id" \ |       order by sepa_mandat_id" \ | ||||||
|      "sepa-mandates.csv" |      "office/sepa_mandates.csv" | ||||||
|  |  | ||||||
| dump "select member_asset_id, bp_id, date, action, amount, comment | dump "select member_asset_id, bp_id, date, action, amount, comment | ||||||
|      from member_asset |      from member_asset | ||||||
|      WHERE bp_id NOT IN (511912) |      WHERE bp_id NOT IN (511912) | ||||||
|      order by member_asset_id" \ |      order by member_asset_id" \ | ||||||
|      "asset-transactions.csv" |      "office/asset_transactions.csv" | ||||||
|  |  | ||||||
| dump "select member_share_id, bp_id, date, action, quantity, comment | dump "select member_share_id, bp_id, date, action, quantity, comment | ||||||
|       from member_share |       from member_share | ||||||
|      WHERE bp_id NOT IN (511912) |      WHERE bp_id NOT IN (511912) | ||||||
|       order by member_share_id" \ |       order by member_share_id" \ | ||||||
|      "share-transactions.csv" |      "office/share_transactions.csv" | ||||||
|  |  | ||||||
| dump "select inet_addr_id, inet_addr, description | dump "select inet_addr_id, inet_addr, description | ||||||
|       from inet_addr |       from inet_addr | ||||||
|       order by inet_addr_id" \ |       order by inet_addr_id" \ | ||||||
|      "inet_addr.csv" |      "hosting/inet_addr.csv" | ||||||
|  |  | ||||||
| dump "select hive_id, hive_name, inet_addr_id, description | dump "select hive_id, hive_name, inet_addr_id, description | ||||||
|       from hive |       from hive | ||||||
|       order by hive_id" \ |       order by hive_id" \ | ||||||
|      "hive.csv" |      "hosting/hive.csv" | ||||||
|  |  | ||||||
| dump "select packet_id, basepacket_code, packet_name, bp_id, hive_id, created, cancelled, cur_inet_addr_id, old_inet_addr_id, free | dump "select packet_id, basepacket_code, packet_name, bp_id, hive_id, created, cancelled, cur_inet_addr_id, old_inet_addr_id, free | ||||||
|       from packet |       from packet | ||||||
|       left join basepacket using (basepacket_id) |       left join basepacket using (basepacket_id) | ||||||
|       order by packet_id" \ |       order by packet_id" \ | ||||||
|      "packet.csv" |      "hosting/packet.csv" | ||||||
|  |  | ||||||
| dump "select packet_component_id, packet_id, quantity, basecomponent_code, created, cancelled | dump "select packet_component_id, packet_id, quantity, basecomponent_code, created, cancelled | ||||||
|       from packet_component |       from packet_component | ||||||
|       left join basecomponent using (basecomponent_id) |       left join basecomponent using (basecomponent_id) | ||||||
|       order by packet_component_id" \ |       order by packet_component_id" \ | ||||||
|      "packet_component.csv" |      "hosting/packet_component.csv" | ||||||
|  |  | ||||||
| dump "select unixuser_id, name, comment, shell, homedir, locked, packet_id, userid, quota_softlimit, quota_hardlimit, storage_softlimit, storage_hardlimit | dump "select unixuser_id, name, comment, shell, homedir, locked, packet_id, userid, quota_softlimit, quota_hardlimit, storage_softlimit, storage_hardlimit | ||||||
|       from unixuser |       from unixuser | ||||||
|       order by unixuser_id" \ |       order by unixuser_id" \ | ||||||
|      "unixuser.csv" |      "hosting/unixuser.csv" | ||||||
|  |  | ||||||
| # weil das fehlt, muss group by komplett gesetzt werden: alter table domain add constraint PK_domain primary key (domain_id); | # weil das fehlt, muss group by komplett gesetzt werden: alter table domain add constraint PK_domain primary key (domain_id); | ||||||
| dump "select domain_id, domain_name, domain_since, domain_dns_master, domain_owner, valid_subdomain_names, passenger_python, passenger_nodejs, passenger_ruby, fcgi_php_bin, array_to_string(array_agg(domain_option_name), ',') as domainoptions | dump "select domain_id, domain_name, domain_since, domain_dns_master, domain_owner, valid_subdomain_names, passenger_python, passenger_nodejs, passenger_ruby, fcgi_php_bin, array_to_string(array_agg(domain_option_name), ',') as domainoptions | ||||||
| @@ -80,7 +80,7 @@ dump "select domain_id, domain_name, domain_since, domain_dns_master, domain_own | |||||||
|       left join domain_option using (domain_option_id) |       left join domain_option using (domain_option_id) | ||||||
|       group by domain.domain_id, domain.domain_name, domain_since, domain_dns_master, domain_owner, valid_subdomain_names, passenger_python, passenger_nodejs, passenger_ruby, fcgi_php_bin |       group by domain.domain_id, domain.domain_name, domain_since, domain_dns_master, domain_owner, valid_subdomain_names, passenger_python, passenger_nodejs, passenger_ruby, fcgi_php_bin | ||||||
|       order by domain.domain_id" \ |       order by domain.domain_id" \ | ||||||
|      "domain.csv" |      "hosting/domain.csv" | ||||||
|  |  | ||||||
| dump "select emailaddr_id, domain_id, localpart, subdomain, target | dump "select emailaddr_id, domain_id, localpart, subdomain, target | ||||||
|       from emailaddr |       from emailaddr | ||||||
| @@ -90,14 +90,14 @@ dump "select emailaddr_id, domain_id, localpart, subdomain, target | |||||||
| dump "select emailalias_id, pac_id, name, target | dump "select emailalias_id, pac_id, name, target | ||||||
|       from emailalias |       from emailalias | ||||||
|       order by emailalias_id" \ |       order by emailalias_id" \ | ||||||
|      "emailalias.csv" |      "hosting/emailalias.csv" | ||||||
|  |  | ||||||
| dump "select dbuser_id, engine, packet_id, name | dump "select dbuser_id, engine, packet_id, name | ||||||
|       from database_user |       from database_user | ||||||
|       order by dbuser_id" \ |       order by dbuser_id" \ | ||||||
|      "database_user.csv" |      "hosting/database_user.csv" | ||||||
|  |  | ||||||
| dump "select database_id, engine, packet_id, name, owner, encoding | dump "select database_id, engine, packet_id, name, owner, encoding | ||||||
|       from database |       from database | ||||||
|       order by database_id" \ |       order by database_id" \ | ||||||
|      "database.csv" |      "hosting/database.csv" | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								src/test/resources/migration/hosting/hive.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/test/resources/migration/hosting/hive.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | hive_id;hive_name;inet_addr_id;description | ||||||
|  | 1;h00;358; | ||||||
|  | 2;h01;359; | ||||||
|  | 4;h02;360; | ||||||
|  | 7;h03;361; | ||||||
|  | 13;h04;430; | ||||||
|  | 14;h50;433; | ||||||
|  | 20;h05;354; | ||||||
|  | 21;h06;355; | ||||||
|  | 22;h07;357; | ||||||
|  | 28;h60;363; | ||||||
|  | 31;h63;431; | ||||||
|  | 37;h67;381; | ||||||
|  | 38;h97;537; | ||||||
|  | 39;h96;536; | ||||||
|  | 45;h74;485; | ||||||
|  | 50;h82;514; | ||||||
|  | 128;h19;565; | ||||||
|  | 148;h50;522; | ||||||
|  | 163;h92;457; | ||||||
|  | 173;h25;1759; | ||||||
|  | 192;h93;1778; | ||||||
|  | 193;h95;1779; | ||||||
|  | 205;vm1107;1861; | ||||||
|  | 208;vm1110;1864; | ||||||
|  | 210;vm1112;1833; | ||||||
| 
 | 
							
								
								
									
										10
									
								
								src/test/resources/migration/hosting/inet_addr.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/test/resources/migration/hosting/inet_addr.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | inet_addr_id;inet_addr;description | ||||||
|  | 363;83.223.95.34; | ||||||
|  | 381;83.223.95.52; | ||||||
|  | 402;83.223.95.73; | ||||||
|  | 433;83.223.95.104; | ||||||
|  | 457;83.223.95.128; | ||||||
|  | 473;83.223.95.144; | ||||||
|  | 574;83.223.95.245; | ||||||
|  | 1168;83.223.79.72; | ||||||
|  | 1790;83.223.94.179; | ||||||
| 
 | 
							
								
								
									
										10
									
								
								src/test/resources/migration/hosting/packet.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/test/resources/migration/hosting/packet.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | packet_id;basepacket_code;packet_name;bp_id;hive_id;created;cancelled;cur_inet_addr_id;old_inet_addr_id;free | ||||||
|  | 630;PAC/WEB;hsh00;213;14;2001-06-01;;473;;1 | ||||||
|  | 968;SRV/MGD;vm1061;132;28;2013-04-01;;363;;0 | ||||||
|  | 978;SRV/MGD;vm1050;213;14;2013-04-01;;433;;1 | ||||||
|  | 1061;SRV/MGD;vm1068;100;37;2013-08-19;;381;;f | ||||||
|  | 1094;PAC/WEB;lug00;100;37;2013-09-10;;1168;;1 | ||||||
|  | 1112;PAC/WEB;mim00;100;37;2013-09-17;;402;;1 | ||||||
|  | 1447;SRV/MGD;vm1093;213;163;2014-11-28;;457;;t | ||||||
|  | 19959;PAC/WEB;dph00;542;163;2021-06-02;;574;;0 | ||||||
|  | 23611;SRV/CLD;vm2097;541;;2022-08-10;;1790;;0 | ||||||
| 
 | 
							
								
								
									
										143
									
								
								src/test/resources/migration/hosting/packet_component.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/test/resources/migration/hosting/packet_component.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | packet_component_id;packet_id;quantity;basecomponent_code;created;cancelled | ||||||
|  | 46105;1094;10;TRAFFIC;2017-03-27; | ||||||
|  | 46109;1094;5;MULTI;2017-03-27; | ||||||
|  | 46111;1094;0;DAEMON;2017-03-27; | ||||||
|  | 46113;1094;1024;QUOTA;2017-03-27; | ||||||
|  | 46117;1112;0;DAEMON;2017-03-27; | ||||||
|  | 46121;1112;20;TRAFFIC;2017-03-27; | ||||||
|  | 46122;1112;5;MULTI;2017-03-27; | ||||||
|  | 46123;1112;3072;QUOTA;2017-03-27; | ||||||
|  | 143133;1094;1;SLABASIC;2017-09-01; | ||||||
|  | 143483;1112;1;SLABASIC;2017-09-01; | ||||||
|  | 757383;1112;0;SLAEXT24H;; | ||||||
|  | 770533;1094;0;SLAEXT24H;; | ||||||
|  | 784283;1112;0;OFFICE;; | ||||||
|  | 797433;1094;0;OFFICE;; | ||||||
|  | 1228033;1112;0;STORAGE;; | ||||||
|  | 1241433;1094;0;STORAGE;; | ||||||
|  | 1266451;978;0;SLAPLAT4H;2021-10-05; | ||||||
|  | 1266452;978;250;TRAFFIC;2021-10-05; | ||||||
|  | 1266453;978;0;SLAPLAT8H;2021-10-05; | ||||||
|  | 1266454;978;0;SLAMAIL4H;2021-10-05; | ||||||
|  | 1266455;978;0;SLAMARIA8H;2021-10-05; | ||||||
|  | 1266456;978;0;SLAPGSQL4H;2021-10-05; | ||||||
|  | 1266457;978;0;SLAWEB4H;2021-10-05; | ||||||
|  | 1266458;978;0;SLAMARIA4H;2021-10-05; | ||||||
|  | 1266459;978;0;SLAPGSQL8H;2021-10-05; | ||||||
|  | 1266460;978;0;SLAOFFIC8H;2021-10-05; | ||||||
|  | 1266461;978;0;SLAWEB8H;2021-10-05; | ||||||
|  | 1266462;978;256000;STORAGE;2021-10-05; | ||||||
|  | 1266463;978;153600;QUOTA;2021-10-05; | ||||||
|  | 1266464;978;0;SLAOFFIC4H;2021-10-05; | ||||||
|  | 1266465;978;32768;RAM;2021-10-05; | ||||||
|  | 1266466;978;4;CPU;2021-10-05; | ||||||
|  | 1266467;978;1;SLABASIC;2021-10-05; | ||||||
|  | 1266468;978;0;SLAMAIL8H;2021-10-05; | ||||||
|  | 1275583;978;0;SLAPLAT2H;2022-04-20; | ||||||
|  | 1280533;978;0;SLAWEB2H;2022-04-20; | ||||||
|  | 1285483;978;0;SLAMARIA2H;2022-04-20; | ||||||
|  | 1290433;978;0;SLAPGSQL2H;2022-04-20; | ||||||
|  | 1295383;978;0;SLAMAIL2H;2022-04-20; | ||||||
|  | 1300333;978;0;SLAOFFIC2H;2022-04-20; | ||||||
|  | 1305933;1447;0;SLAWEB2H;2022-05-02; | ||||||
|  | 1305934;1447;0;SLAPLAT4H;2022-05-02; | ||||||
|  | 1305935;1447;0;SLAWEB8H;2022-05-02; | ||||||
|  | 1305936;1447;0;SLAOFFIC4H;2022-05-02; | ||||||
|  | 1305937;1447;0;SLAMARIA4H;2022-05-02; | ||||||
|  | 1305938;1447;0;SLAOFFIC8H;2022-05-02; | ||||||
|  | 1305939;1447;1;SLABASIC;2022-05-02; | ||||||
|  | 1305940;1447;0;SLAMAIL8H;2022-05-02; | ||||||
|  | 1305941;1447;0;SLAPGSQL4H;2022-05-02; | ||||||
|  | 1305942;1447;6;CPU;2022-05-02; | ||||||
|  | 1305943;1447;250;TRAFFIC;2022-05-02; | ||||||
|  | 1305944;1447;0;SLAOFFIC2H;2022-05-02; | ||||||
|  | 1305945;1447;0;SLAMAIL4H;2022-05-02; | ||||||
|  | 1305946;1447;0;SLAPGSQL2H;2022-05-02; | ||||||
|  | 1305947;1447;0;SLAMARIA2H;2022-05-02; | ||||||
|  | 1305948;1447;0;SLAMARIA8H;2022-05-02; | ||||||
|  | 1305949;1447;0;SLAWEB4H;2022-05-02; | ||||||
|  | 1305950;1447;16384;RAM;2022-05-02; | ||||||
|  | 1305951;1447;0;SLAPGSQL8H;2022-05-02; | ||||||
|  | 1305952;1447;512000;STORAGE;2022-05-02; | ||||||
|  | 1305953;1447;0;SLAMAIL2H;2022-05-02; | ||||||
|  | 1305954;1447;0;SLAPLAT2H;2022-05-02; | ||||||
|  | 1305955;1447;0;SLAPLAT8H;2022-05-02; | ||||||
|  | 1305956;1447;307200;QUOTA;2022-05-02; | ||||||
|  | 1312013;23611;1;SLABASIC;2022-08-10; | ||||||
|  | 1312014;23611;0;BANDWIDTH;2022-08-10; | ||||||
|  | 1312015;23611;12288;RAM;2022-08-10; | ||||||
|  | 1312016;23611;25600;QUOTA;2022-08-10; | ||||||
|  | 1312017;23611;0;SLAINFR8H;2022-08-10; | ||||||
|  | 1312018;23611;0;STORAGE;2022-08-10; | ||||||
|  | 1312019;23611;0;SLAINFR2H;2022-08-10; | ||||||
|  | 1312020;23611;8;CPU;2022-08-10; | ||||||
|  | 1312021;23611;250;TRAFFIC;2022-08-10; | ||||||
|  | 1312022;23611;0;SLAINFR4H;2022-08-10; | ||||||
|  | 1313883;978;0;BANDWIDTH;; | ||||||
|  | 1316583;1447;0;BANDWIDTH;; | ||||||
|  | 1338074;968;0;SLAMARIA2H;2023-09-05; | ||||||
|  | 1338075;968;384000;QUOTA;2023-09-05; | ||||||
|  | 1338076;968;1;SLAMAIL8H;2023-09-05; | ||||||
|  | 1338077;968;0;BANDWIDTH;2023-09-05; | ||||||
|  | 1338078;968;0;SLAWEB2H;2023-09-05; | ||||||
|  | 1338079;968;0;SLAOFFIC4H;2023-09-05; | ||||||
|  | 1338080;968;256000;STORAGE;2023-09-05; | ||||||
|  | 1338081;968;0;SLAPLAT4H;2023-09-05; | ||||||
|  | 1338082;968;0;SLAPGSQL2H;2023-09-05; | ||||||
|  | 1338083;968;0;SLAPLAT2H;2023-09-05; | ||||||
|  | 1338084;968;250;TRAFFIC;2023-09-05; | ||||||
|  | 1338085;968;1;SLAMARIA8H;2023-09-05; | ||||||
|  | 1338086;968;0;SLAPGSQL4H;2023-09-05; | ||||||
|  | 1338087;968;0;SLAMAIL2H;2023-09-05; | ||||||
|  | 1338088;968;1;SLAPLAT8H;2023-09-05; | ||||||
|  | 1338089;968;0;SLAWEB4H;2023-09-05; | ||||||
|  | 1338090;968;6;CPU;2023-09-05; | ||||||
|  | 1338091;968;1;SLAPGSQL8H;2023-09-05; | ||||||
|  | 1338092;968;0;SLAMARIA4H;2023-09-05; | ||||||
|  | 1338093;968;0;SLAMAIL4H;2023-09-05; | ||||||
|  | 1338094;968;14336;RAM;2023-09-05; | ||||||
|  | 1338095;968;0;SLAOFFIC2H;2023-09-05; | ||||||
|  | 1338096;968;0;SLAOFFIC8H;2023-09-05; | ||||||
|  | 1338097;968;1;SLABASIC;2023-09-05; | ||||||
|  | 1338098;968;1;SLAWEB8H;2023-09-05; | ||||||
|  | 1339228;19959;20;TRAFFIC;2023-10-27; | ||||||
|  | 1339229;19959;1;SLABASIC;2023-10-27; | ||||||
|  | 1339230;19959;0;DAEMON;2023-10-27; | ||||||
|  | 1339231;19959;25600;QUOTA;2023-10-27; | ||||||
|  | 1339232;19959;0;STORAGE;2023-10-27; | ||||||
|  | 1339233;19959;0;SLAEXT24H;2023-10-27; | ||||||
|  | 1339234;19959;0;OFFICE;2023-10-27; | ||||||
|  | 1339235;19959;1;MULTI;2023-10-27; | ||||||
|  | 1341088;1061;0;SLAOFFIC2H;2023-12-14; | ||||||
|  | 1341089;1061;0;SLAOFFIC8H;2023-12-14; | ||||||
|  | 1341090;1061;256000;STORAGE;2023-12-14; | ||||||
|  | 1341091;1061;0;SLAMAIL4H;2023-12-14; | ||||||
|  | 1341092;1061;0;SLAMAIL2H;2023-12-14; | ||||||
|  | 1341093;1061;0;SLAPLAT2H;2023-12-14; | ||||||
|  | 1341094;1061;4096;RAM;2023-12-14; | ||||||
|  | 1341095;1061;0;SLAPLAT4H;2023-12-14; | ||||||
|  | 1341096;1061;1;SLAPGSQL8H;2023-12-14; | ||||||
|  | 1341097;1061;2;CPU;2023-12-14; | ||||||
|  | 1341098;1061;0;QUOTA;2023-12-14; | ||||||
|  | 1341099;1061;0;SLAMAIL8H;2023-12-14; | ||||||
|  | 1341100;1061;1;SLABASIC;2023-12-14; | ||||||
|  | 1341101;1061;1;SLAMARIA8H;2023-12-14; | ||||||
|  | 1341102;1061;0;SLAPGSQL4H;2023-12-14; | ||||||
|  | 1341103;1061;0;SLAPGSQL2H;2023-12-14; | ||||||
|  | 1341104;1061;0;SLAMARIA4H;2023-12-14; | ||||||
|  | 1341105;1061;0;SLAOFFIC4H;2023-12-14; | ||||||
|  | 1341106;1061;1;SLAPLAT8H;2023-12-14; | ||||||
|  | 1341107;1061;0;BANDWIDTH;2023-12-14; | ||||||
|  | 1341108;1061;1;SLAWEB8H;2023-12-14; | ||||||
|  | 1341109;1061;0;SLAWEB2H;2023-12-14; | ||||||
|  | 1341110;1061;0;SLAMARIA2H;2023-12-14; | ||||||
|  | 1341111;1061;250;TRAFFIC;2023-12-14; | ||||||
|  | 1341112;1061;0;SLAWEB4H;2023-12-14; | ||||||
|  | 1346628;630;0;SLAEXT24H;2024-03-19; | ||||||
|  | 1346629;630;0;OFFICE;2024-03-19; | ||||||
|  | 1346630;630;16384;QUOTA;2024-03-19; | ||||||
|  | 1346631;630;0;DAEMON;2024-03-19; | ||||||
|  | 1346632;630;10240;STORAGE;2024-03-19; | ||||||
|  | 1346633;630;1;SLABASIC;2024-03-19; | ||||||
|  | 1346634;630;50;TRAFFIC;2024-03-19; | ||||||
|  | 1346635;630;25;MULTI;2024-03-19; | ||||||
| 
 | 
							
								
								
									
										19
									
								
								src/test/resources/migration/office/asset_transactions.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/test/resources/migration/office/asset_transactions.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | member_asset_id;		bp_id;		date;		action;	  amount;	comment | ||||||
|  | 358;					100;		2000-12-06;	PAYMENT;	5120; 	for subscription A | ||||||
|  | 442;					132;		2003-07-07;	PAYMENT;	  64; | ||||||
|  | 577;					100;		2011-12-12;	PAYMENT;	1024; | ||||||
|  | 632;					132;		2013-10-21;	PAYMENT;	  64; | ||||||
|  | 885;					100;		2020-12-15;	PAYMENT;	6144;	Einzahlung | ||||||
|  | 924;					541;		2021-05-21;	PAYMENT;	 256;	Beitritt - Lastschrift | ||||||
|  | 925;					542;		2021-05-31;	PAYMENT;	  64;	Beitritt - Lastschrift | ||||||
|  | 1093;					100;		2023-10-05;	PAYMENT;	3072;	Kapitalerhoehung - Ueberweisung | ||||||
|  | 1094;					100;		2023-10-06;	PAYMENT;	3072;	Kapitalerhoehung - Ueberweisung | ||||||
|  | 31000;				 	120;		2000-12-06;	PAYMENT;  	128.00; for subscription B | ||||||
|  | 32000;				 	100;		2005-01-10;	PAYMENT;   2560.00; for subscription C | ||||||
|  | 33001;				 	100;		2005-01-10;	HANDOVER;  -512.00; for transfer to 10 | ||||||
|  | 33002;				 	120;		2005-01-10;	ADOPTION; 	512.00; for transfer from 7 | ||||||
|  | 34001;				 	120;		2016-12-31;	CLEARING; 	 -8.00; for cancellation D | ||||||
|  | 34002;				 	120;		2016-12-31;	PAYBACK;   -100.00; for cancellation D | ||||||
|  | 34003;				 	120;		2016-12-31;	LOSS;	    -20.00; for cancellation D | ||||||
|  | 35001;				 	190;		2024-01-15;	PAYMENT; 	128.00; for subscription E | ||||||
|  | 35002;				 	190;		2024-01-20;	ADJUSTMENT;-128.00; chargeback for subscription E | ||||||
| 
 | 
							
								
								
									
										10
									
								
								src/test/resources/migration/office/business_partners.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/test/resources/migration/office/business_partners.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | bp_id;member_id;member_code;member_since;member_until;member_role;author_contract;nondisc_contract;free;exempt_vat;indicator_vat;uid_vat | ||||||
|  | 100;10003;hsh00-mim;2000-12-06;;Aufsichtsrat;;2001-04-24;0;0;GROSS;DE217249198 | ||||||
|  | 132;10152;hsh00-rar;2003-07-12;;;;;0;0;GROSS;DE 236 109 136 | ||||||
|  | 213;10000;hsh00-hsh;;;Hostsharing eG;;;1;0;GROSS; | ||||||
|  | 541;11018;hsh00-wws;2021-05-17;;;;;0;0;GROSS; | ||||||
|  | 542;11019;hsh00-dph;2021-05-25;;;;;0;0;GROSS; | ||||||
|  | 120;10020;hsh00-xyz;2000-12-06;2015-12-31;;;;false;false;GROSS; | ||||||
|  | 122;11022;hsh00-xxx;2021-04-01;;;;;true;true;GROSS; | ||||||
|  | 190;19090;hsh00-yyy;;;;;;true;true;GROSS; | ||||||
|  | 199;19999;hsh00-zzz;;;;;;false;false;GROSS; | ||||||
| 
 | 
							
								
								
									
										35
									
								
								src/test/resources/migration/office/contacts.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/test/resources/migration/office/contacts.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | contact_id;	bp_id;	salut;	first_name;	last_name;	title;	firma;	co;	street;	zipcode;	city;	country;	phone_private;	phone_office;	phone_mobile;	fax;	email;	roles | ||||||
|  |  | ||||||
|  | # Hostsharing, the mandate itself | ||||||
|  | 212;	213;	Firma;	Hostmaster;	Hostsharing;	;	Hostsharing e.G.;	;	;	;	;	Germany;	;	;	;	;	hostmaster@hostsharing.net;	billing,operation,contractual,partner | ||||||
|  |  | ||||||
|  | # some natural persons | ||||||
|  | 100;	100;	Herr;	Michael;	Mellis;	;	Michael Mellis;	;	Dr. Bolte Str. 50;	26524;	Hage;	Germany;	;	+49 4931/1234567;	+49/1522123455;	+49 40 912345-9;	michael@Mellis.example.org;	billing,operation,contractual,partner,subscriber:members-announce,subscriber:operations-announce,subscriber:operations-discussion,subscriber:members-discussion,subscriber:generalversammlung | ||||||
|  | 132;	132;	Herr;	Ragnar;	Richter;	;	Ragnar IT-Beratung;	;	Vioktoriastraße 114;	70197;	Stuttgart;	Germany;	+49 711 987654-1;	+49 711 987654-2;	;	+49 711 87654-3;	hostsharing@ragnar-richter.de;	billing,operation,partner,subscriber:operations-announce,subscriber:operations-discussion | ||||||
|  |  | ||||||
|  | # eine juristische Person mit drei separaten Ansprechpartnern, vip-contact und ex-partner | ||||||
|  | 1200;	120;	;	;	;	;	JM e.K.;	;	Wiesenweg 15;	12335;	Berlin;	DE;	+49 30 6666666;	+49 30 5555555;	;	+49 30 6666666;	jm-ex-partner@example.org;	ex-partner | ||||||
|  | 1201;	120;	Frau;	Jenny;	Meyer-Billing;	Dr.;	JM GmbH;	;	Waldweg 5;	11001;	Berlin;	DE;	+49 30 7777777;	+49 30 1111111;	;	+49 30 2222222;	jm-billing@example.org;	billing | ||||||
|  | 1202;	120;	Herr;	Andrew;	Meyer-Operation;	;	JM GmbH;	;	Waldweg 5;	11001;	Berlin;	DE;	+49 30 6666666;	+49 30 3333333;	;	+49 30 4444444;	am-operation@example.org;	operation,vip-contact,subscriber:operations-announce | ||||||
|  | 1203;	120;	Herr;	Philip;	Meyer-Contract;	;	JM GmbH;	;	Waldweg 5;	11001;	Berlin;	DE;	+49 30 6666666;	+49 30 5555555;	;	+49 30 6666666;	pm-partner@example.org;	partner,contractual,subscriber:members-announce,subscriber:customers-announce | ||||||
|  | 1204;	120;	Frau;	Tammy;	Meyer-VIP;	;	JM GmbH;	;	Waldweg 5;	11001;	Berlin;	DE;	+49 30 999999;	+49 30 999999;	;	+49 30 6666666;	tm-vip@example.org;	vip-contact | ||||||
|  |  | ||||||
|  | # eine juristische Person mit nur einem Ansprechpartner und explizitem contractual | ||||||
|  | 1301;	122;	;	Petra;	Schmidt;	;	Test PS;	;	;	;	;	;	;	;	;	;	ps@example.com;	partner,billing,contractual,operation | ||||||
|  |  | ||||||
|  | # eine natürliche Person, die nur Subscriber ist | ||||||
|  | 1401;	120;	Frau;	Frauke;	Fanninga;	;	;	;	Am Walde 1;	29456;	Hitzacker;	DE;	;	;	;	;	ff@example.org;	subscriber:operations-announce | ||||||
|  |  | ||||||
|  | # eine natürliche Person als Partner | ||||||
|  | 1501;	190;	Frau;	Cecilia;	Camus;	;	;	;	Rue d'Avignion 60;	45000;	Orléans;	FR;	;	;	;	;	cc@example.org;	partner,contractual,billing,operation | ||||||
|  |  | ||||||
|  | # some more contacts of realistic business partners | ||||||
|  |  | ||||||
|  | 90436;	541;	Frau;	Christiane;	Milberg;	;	Wasserwerk Südholstein;	;	Am Wasserwerk 1-3;	25491;	Hetlingen;	Germany;	;	;	+49 4103 12345-1;	;	rechnung@ww-sholst.example.org;	billing,partner,subscriber:members-discussion,contractual,subscriber:members-announce,subscriber:generalversammlung | ||||||
|  | 90437;	542;	Herr;	Richard;	Wiese;	;	Das Perfekte Haus;	;	Kennedyplatz 11;	45279;	Essen;	Germany;	;	;	+49-172-12345;	;	admin@das-perfekte-haus.example.org;	operation,partner,subscriber:members-discussion,contractual,subscriber:operations-announce,subscriber:operations-discussion,subscriber:members-announce,subscriber:generalversammlung | ||||||
|  | 90438;	541;	Herr;	Karim;	Metzger;	;	Wasswerwerk Südholstein;	;	Am Wasserwerk 1-3;	25491;	Hetlingen;	Germany;	;	+49 4103 12345-2;	;	;	karim.metzger@ww-sholst.example.org;	operation,subscriber:operations-announce,subscriber:operations-discussion | ||||||
|  | 90590;	542;	Herr;	Inhaber R.;	Wiese;	;	Das Perfekte Haus;	Client-ID 515217;	Essen, Kastanienallee 81;	30127;	Hannover;	Germany;	;	;	;	;	515217@kkemail.example.org;	billing | ||||||
|  | 90629;	132;	;	Ragnar;	Richter;	;	;	;	;	;	;	;	;	;	;	;	mail@ragnar-richter..example.org;	contractual,subscriber:members-announce,subscriber:members-discussion,subscriber:generalversammlung | ||||||
|  | 90677;	132;	;	Eike;	Henning;	;	;	;	;	;	;	;	;	;	;	;	hostsharing@eike-henning..example.org;	operation,subscriber:operations-announce,subscriber:operations-discussion | ||||||
|  | 90698;	132;	;	Jan;	Henning;	;	;	;	;	;	;	;	;	01577 12345678;	;	;	mail@jan-henning.example.org;	operation | ||||||
|  |  | ||||||
| 
 | 
							
								
								
									
										8
									
								
								src/test/resources/migration/office/sepa_mandates.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/test/resources/migration/office/sepa_mandates.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | sepa_mandat_id;bp_id;bank_customer;bank_name;bank_iban;bank_bic;mandat_ref;mandat_signed;mandat_since;mandat_until;mandat_used | ||||||
|  | 30;132;Ragnar Richter;GLS Gemeinschaftsbank eG;DE02300209000106531065;GENODEM1GLS;HS-10152-20140801;2013-12-01;2013-12-01;2016-02-15;2014-01-20 | ||||||
|  | 132;100;Michael Mellis;Hamburger Volksbank;DE37500105177419788228;GENODEF1HH2;HS-10003-20140801;2013-12-01;2013-12-01;;2022-12-31 | ||||||
|  | 386;541;Wasserwerk Suedholstein;Sparkasse Westholstein;DE49500105174516484892;NOLADE21WHO;HS-11018-20210512;2021-05-12;2021-05-17;;2022-12-31 | ||||||
|  | 387;542;Richard Wiese Das Perfekte Haus;Commerzbank Wuppertal;DE89370400440532013000;COBADEFFXXX;HS-11019-20210519;2021-05-19;2021-05-25;;2022-12-31 | ||||||
|  | 234234;100;Michael Mellis;ING Bank AG;DE37500105177419788228;INGDDEFFXXX;MH12345;2004-06-12;2004-06-15;;2022-10-20 | ||||||
|  | 235600;120;JM e.K.;Targobank AG;DE02300209000106531065;CMCIDEDD;JM33344;2004-01-15;2004-01-20;2005-06-27	;2016-01-18 | ||||||
|  | 235662;120;JM GmbH;ING Bank AG;DE49500105174516484892;INGDDEFFXXX;JM33344;2005-06-28;2005-07-01;;2016-01-18 | ||||||
| 
 | 
							
								
								
									
										12
									
								
								src/test/resources/migration/office/share_transactions.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/test/resources/migration/office/share_transactions.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | member_share_id;bp_id;date;action;quantity;comment | ||||||
|  | 3;100;2000-12-06;SUBSCRIPTION;80;initial share subscription | ||||||
|  | 90;132;2003-07-12;SUBSCRIPTION;1; | ||||||
|  | 241;100;2011-12-05;SUBSCRIPTION;16; | ||||||
|  | 279;132;2013-10-21;SUBSCRIPTION;1; | ||||||
|  | 523;100;2020-12-08;SUBSCRIPTION;96;Kapitalerhoehung | ||||||
|  | 562;541;2021-05-17;SUBSCRIPTION;4;Beitritt | ||||||
|  | 563;542;2021-05-25;SUBSCRIPTION;1;Beitritt | ||||||
|  | 721;100;2023-10-10;SUBSCRIPTION;96;Kapitalerhoehung | ||||||
|  | 33451;120;2000-12-06;SUBSCRIPTION;2;initial share subscription | ||||||
|  | 33701;100;2005-01-10;SUBSCRIPTION;40;increase | ||||||
|  | 33810;120;2016-12-31;UNSUBSCRIPTION;22;membership ended | ||||||
| 
 | 
| @@ -1,4 +0,0 @@ | |||||||
| sepa_mandat_id;	bp_id;	bank_customer;	bank_name;	bank_iban;		bank_bic;	mandat_ref;	mandat_signed;	mandat_since;	mandat_until;	mandat_used |  | ||||||
| 234234;		17;	Michael Mellies;	ING Bank AG;	DE37500105177419788228;	INGDDEFFXXX;	MH12345;	2004-06-12;	2004-06-15;	;	2022-10-20 |  | ||||||
| 235600;		20;	JM e.K.;	Targobank AG;	DE02300209000106531065;	CMCIDEDD;	JM33344;	2004-01-15;	2004-01-20;2005-06-27	;2016-01-18 |  | ||||||
| 235662;		20;	JM GmbH;	ING Bank AG;	DE49500105174516484892;	INGDDEFFXXX;	JM33344;	2005-06-28;	2005-07-01;	;	2016-01-18 |  | ||||||
| 
 | 
| @@ -1,5 +0,0 @@ | |||||||
| member_share_id;bp_id;	date;		action;		quantity;	comment |  | ||||||
| 33443;		17;	2000-12-06;	SUBSCRIPTION;	20;		initial share subscription |  | ||||||
| 33451;		20;	2000-12-06;	SUBSCRIPTION;	2;		initial share subscription |  | ||||||
| 33701;		17;	2005-01-10;	SUBSCRIPTION;	40;		increase |  | ||||||
| 33810;		20;	2016-12-31;	UNSUBSCRIPTION;	22;		membership ended |  | ||||||
| 
 | 
		Reference in New Issue
	
	Block a user