FINERACT-2245: New command processing - mix taxonomy

This commit is contained in:
Aleksandar Vidakovic
2026-02-18 20:26:51 +01:00
committed by Aleksandar Vidakovic
parent fe98aa6a58
commit ae3e2a6343
54 changed files with 1037 additions and 751 deletions
+1
View File
@@ -42,6 +42,7 @@ buildscript {
'fineract-loan', 'fineract-loan',
'fineract-savings', 'fineract-savings',
'fineract-report', 'fineract-report',
'fineract-mix',
'integration-tests', 'integration-tests',
'twofactor-tests', 'twofactor-tests',
'oauth2-tests', 'oauth2-tests',
@@ -426,13 +426,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateCurrencies() {
this.actionName = "UPDATE";
this.entityName = "CURRENCY";
this.href = "/currencies";
return this;
}
public CommandWrapperBuilder createSms() { public CommandWrapperBuilder createSms() {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "SMS"; this.entityName = "SMS";
@@ -1862,55 +1855,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder createNote(final CommandWrapper resourceDetails, final String resourceType, final Long resourceId) {
this.actionName = "CREATE";
this.entityName = resourceDetails.entityName();// Note supports multiple
// resources. Note
// Permissions are set
// for each resource.
this.clientId = resourceDetails.getClientId();
this.loanId = resourceDetails.getLoanId();
this.savingsId = resourceDetails.getSavingsId();
this.groupId = resourceDetails.getGroupId();
this.subentityId = resourceDetails.subresourceId();
this.href = "/" + resourceType + "/" + resourceId + "/notes/template";
return this;
}
public CommandWrapperBuilder updateNote(final CommandWrapper resourceDetails, final String resourceType, final Long resourceId,
final Long noteId) {
this.actionName = "UPDATE";
this.entityName = resourceDetails.entityName();// Note supports multiple
// resources. Note
// Permissions are set
// for each resource.
this.entityId = noteId;
this.clientId = resourceDetails.getClientId();
this.loanId = resourceDetails.getLoanId();
this.savingsId = resourceDetails.getSavingsId();
this.groupId = resourceDetails.getGroupId();
this.subentityId = resourceDetails.subresourceId();
this.href = "/" + resourceType + "/" + resourceId + "/notes";
return this;
}
public CommandWrapperBuilder deleteNote(final CommandWrapper resourceDetails, final String resourceType, final Long resourceId,
final Long noteId) {
this.actionName = "DELETE";
this.entityName = resourceDetails.entityName();// Note supports multiple
// resources. Note
// Permissions are set
// for each resource.
this.entityId = noteId;
this.clientId = resourceDetails.getClientId();
this.loanId = resourceDetails.getLoanId();
this.savingsId = resourceDetails.getSavingsId();
this.groupId = resourceDetails.getGroupId();
this.subentityId = resourceDetails.subresourceId();
this.href = "/" + resourceType + "/" + resourceId + "/calendars/" + noteId;
return this;
}
public CommandWrapperBuilder createGroup() { public CommandWrapperBuilder createGroup() {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "GROUP"; this.entityName = "GROUP";
@@ -2193,14 +2137,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateTaxonomyMapping(final Long mappingId) {
this.actionName = "UPDATE";
this.entityName = "XBRLMAPPING";
this.entityId = mappingId;
this.href = "/xbrlmapping";
return this;
}
public CommandWrapperBuilder createHoliday() { public CommandWrapperBuilder createHoliday() {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "HOLIDAY"; this.entityName = "HOLIDAY";
@@ -2387,13 +2323,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateCache() {
this.actionName = "UPDATE";
this.entityName = "CACHE";
this.href = "/cache";
return this;
}
/** /**
* Deposit account mappings * Deposit account mappings
*/ */
@@ -3582,13 +3511,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateBusinessDate() {
this.actionName = "UPDATE";
this.entityName = "BUSINESS_DATE";
this.href = "/businessdate";
return this;
}
public CommandWrapperBuilder createDelinquencyRange() { public CommandWrapperBuilder createDelinquencyRange() {
this.actionName = "CREATE"; this.actionName = "CREATE";
this.entityName = "DELINQUENCY_RANGE"; this.entityName = "DELINQUENCY_RANGE";
@@ -3651,13 +3573,6 @@ public class CommandWrapperBuilder {
return this; return this;
} }
public CommandWrapperBuilder updateExternalEventConfigurations() {
this.actionName = "UPDATE";
this.entityName = "EXTERNAL_EVENT_CONFIGURATION";
this.href = "/externaleventconfiguration";
return this;
}
public CommandWrapperBuilder chargeOff(final Long loanId) { public CommandWrapperBuilder chargeOff(final Long loanId) {
this.actionName = "CHARGEOFF"; this.actionName = "CHARGEOFF";
this.entityName = "LOAN"; this.entityName = "LOAN";
+75
View File
@@ -0,0 +1,75 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
description = 'Fineract Mix'
apply plugin: 'java'
apply plugin: 'eclipse'
compileJava {
dependsOn ':fineract-avro-schemas:buildJavaSdk'
}
configurations {
providedRuntime // needed for Spring Boot executable WAR
providedCompile
compile() {
exclude module: 'hibernate-entitymanager'
exclude module: 'hibernate-validator'
exclude module: 'activation'
exclude module: 'bcmail-jdk14'
exclude module: 'bcprov-jdk14'
exclude module: 'bctsp-jdk14'
exclude module: 'c3p0'
exclude module: 'stax-api'
exclude module: 'jaxb-api'
exclude module: 'jaxb-impl'
exclude module: 'jboss-logging'
exclude module: 'itext-rtf'
exclude module: 'classworlds'
}
runtime
}
apply from: 'dependencies.gradle'
// Configuration for the modernizer plugin
// https://github.com/andygoossens/gradle-modernizer-plugin
modernizer {
ignoreClassNamePatterns = [
'.*AbstractPersistableCustom',
'.*EntityTables',
'.*domain.*'
]
}
// If we are running Gradle within Eclipse to enhance classes with OpenJPA,
// set the classes directory to point to Eclipse's default build directory
if (project.hasProperty('env') && project.getProperty('env') == 'eclipse') {
sourceSets.main.java.outputDir = new File(rootProject.projectDir, "fineract-core/bin/main")
}
if (!(project.hasProperty('env') && project.getProperty('env') == 'dev')) {
sourceSets {
test {
java {
exclude '**/core/boot/tests/**'
}
}
}
}
+70
View File
@@ -0,0 +1,70 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
dependencies {
// Never use "compile" scope, but make all dependencies either 'implementation', 'runtimeOnly' or 'testCompile'.
// Note that we never use 'api', because Fineract at least currently is a simple monolithic application ("WAR"), not a library.
// We also (normally should have) no need to ever use 'compileOnly'.
// implementation dependencies are directly used (compiled against) in src/main (and src/test)
//
implementation(
project(path: ':fineract-core'),
project(path: ':fineract-command'),
)
implementation(
'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-validation',
'org.springframework.boot:spring-boot-starter-security',
'org.springframework.boot:spring-boot-starter-data-jdbc',
'jakarta.ws.rs:jakarta.ws.rs-api',
'org.apache.commons:commons-lang3',
'com.google.guava:guava',
// TODO: try to get rid of this as soon as we get hands on the MIX XSD file from 2009; then we can use JAXB
'com.google.code.gson:gson',
'com.github.spotbugs:spotbugs-annotations',
'io.swagger.core.v3:swagger-annotations-jakarta',
'org.mapstruct:mapstruct',
'io.github.resilience4j:resilience4j-spring-boot3',
)
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor'
implementation('org.dom4j:dom4j') {
exclude group: 'javax.xml.bind'
}
// testCompile dependencies are ONLY used in src/test, not src/main.
// Do NOT repeat dependencies which are ALREADY in implementation or runtimeOnly!
//
testImplementation( 'io.github.classgraph:classgraph' )
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'com.jayway.jsonpath', module: 'json-path'
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
exclude group: 'jakarta.activation'
exclude group: 'javax.activation'
exclude group: 'org.skyscreamer'
}
testImplementation ('org.mockito:mockito-inline')
}
@@ -26,27 +26,28 @@ import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import java.sql.Date; import java.sql.Date;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.mix.data.XBRLData; import org.apache.fineract.mix.service.MixReportXBRLBuilder;
import org.apache.fineract.mix.service.XBRLBuilder; import org.apache.fineract.mix.service.MixReportXBRLResultService;
import org.apache.fineract.mix.service.XBRLResultService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/mixreport") @Path("/v1/mixreport")
@Component @Component
@Tag(name = "Mix Report", description = "") @Tag(name = "Mix Report", description = """
""")
@RequiredArgsConstructor @RequiredArgsConstructor
public class MixReportApiResource { public class MixReportApiResource {
private final XBRLResultService xbrlResultService; private final MixReportXBRLResultService xbrlResultService;
private final XBRLBuilder xbrlBuilder; private final MixReportXBRLBuilder xbrlBuilder;
@GET @GET
@Produces({ MediaType.APPLICATION_XML }) @Produces({ MediaType.APPLICATION_XML })
public String retrieveXBRLReport(@QueryParam("startDate") final Date startDate, @QueryParam("endDate") final Date endDate, public String retrieveXBRLReport(@QueryParam("startDate") final Date startDate, @QueryParam("endDate") final Date endDate,
@QueryParam("currency") final String currency) { @QueryParam("currency") final String currency) {
final XBRLData data = this.xbrlResultService.getXBRLResult(startDate, endDate, currency); final var data = xbrlResultService.getXBRLResult(startDate, endDate, currency);
// TODO: make this type safe?
return this.xbrlBuilder.build(data); return this.xbrlBuilder.build(data);
} }
} }
@@ -26,9 +26,8 @@ import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.mix.data.MixTaxonomyData; import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.service.MixTaxonomyReadPlatformService; import org.apache.fineract.mix.service.MixTaxonomyReadService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/mixtaxonomy") @Path("/v1/mixtaxonomy")
@@ -37,17 +36,12 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class MixTaxonomyApiResource { public class MixTaxonomyApiResource {
private final PlatformSecurityContext context; private final MixTaxonomyReadService readTaxonomyService;
private final MixTaxonomyReadPlatformService readTaxonomyService;
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public List<MixTaxonomyData> retrieveAll() { public List<MixTaxonomyData> retrieveAll() {
// FIXME - KW - no check for permission to read mix taxonomy data.
this.context.authenticatedUser();
return readTaxonomyService.retrieveAll(); return readTaxonomyService.retrieveAll();
} }
} }
@@ -25,16 +25,14 @@ import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path; import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import java.util.function.Supplier;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper; import org.apache.fineract.command.core.CommandPipeline;
import org.apache.fineract.commands.service.CommandWrapperBuilder; import org.apache.fineract.mix.command.MixTaxonomyMappingUpdateCommand;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.mix.data.MixTaxonomyMappingData; import org.apache.fineract.mix.data.MixTaxonomyMappingData;
import org.apache.fineract.mix.data.MixTaxonomyRequest; import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
import org.apache.fineract.mix.service.MixTaxonomyMappingReadPlatformService; import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateResponse;
import org.apache.fineract.mix.service.MixTaxonomyMappingReadService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/mixmapping") @Path("/v1/mixmapping")
@@ -43,31 +41,31 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class MixTaxonomyMappingApiResource { public class MixTaxonomyMappingApiResource {
private final PlatformSecurityContext context; private final MixTaxonomyMappingReadService readTaxonomyMappingService;
private final ToApiJsonSerializer<MixTaxonomyMappingData> toApiJsonSerializer; private final CommandPipeline commandPipeline;
private final MixTaxonomyMappingReadPlatformService readTaxonomyMappingService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@GET @GET
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public MixTaxonomyMappingData retrieveTaxonomyMapping() { public MixTaxonomyMappingData retrieveTaxonomyMapping() {
this.context.authenticatedUser();
return this.readTaxonomyMappingService.retrieveTaxonomyMapping(); return this.readTaxonomyMappingService.retrieveTaxonomyMapping();
} }
@PUT @PUT
@Consumes({ MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON })
public String updateTaxonomyMapping(final MixTaxonomyRequest mixTaxonomyRequest) { public MixTaxonomyMappingUpdateResponse updateTaxonomyMapping(final MixTaxonomyMappingUpdateRequest request) {
// TODO support multiple configuration file loading // TODO support multiple configuration file loading; this is the legacy behavior
final Long mappingId = (long) 1; if (request.getId() == null) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxonomyMapping(mappingId) request.setId(1L);
.withJson(toApiJsonSerializer.serialize(mixTaxonomyRequest)).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
} }
final var command = new MixTaxonomyMappingUpdateCommand();
command.setPayload(request);
final Supplier<MixTaxonomyMappingUpdateResponse> response = commandPipeline.send(command);
return response.get();
}
} }
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
@Data
@EqualsAndHashCode(callSuper = true)
public class MixTaxonomyMappingUpdateCommand extends Command<MixTaxonomyMappingUpdateRequest> {}
@@ -0,0 +1,42 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.data;
import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class MixReportXBRLContextData implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String dimensionType;
private String dimension;
private Integer periodType;
}
@@ -18,19 +18,28 @@
*/ */
package org.apache.fineract.mix.data; package org.apache.fineract.mix.data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.Date; import java.sql.Date;
import java.util.HashMap; import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@Builder
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
public class XBRLData { public class MixReportXBRLData implements Serializable {
private HashMap<MixTaxonomyData, BigDecimal> resultMap; @Serial
private static final long serialVersionUID = 1L;
private Map<MixTaxonomyData, BigDecimal> resultMap;
private Date startDate; private Date startDate;
private Date endDate; private Date endDate;
private String currency; private String currency;
@@ -18,18 +18,25 @@
*/ */
package org.apache.fineract.mix.data; package org.apache.fineract.mix.data;
import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@Builder
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
public class NamespaceData { public class MixReportXBRLNamespaceData implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@SuppressWarnings("unused")
private Long id; private Long id;
@SuppressWarnings("unused")
private String prefix; private String prefix;
private String url; private String url;
} }
@@ -19,29 +19,37 @@
package org.apache.fineract.mix.data; package org.apache.fineract.mix.data;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@Builder
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
public class MixTaxonomyData { public class MixTaxonomyData implements Serializable {
public static final Integer PORTFOLIO = 0; public static final Integer PORTFOLIO = 0;
public static final Integer BALANCESHEET = 1; public static final Integer BALANCE_SHEET = 1;
public static final Integer INCOME = 2; public static final Integer INCOME = 2;
public static final Integer EXPENSE = 3; public static final Integer EXPENSE = 3;
@SuppressWarnings("unused") @Serial
private static final long serialVersionUID = 1L;
private Long id; private Long id;
private String name; private String name;
private String namespace; private String namespace;
private String dimension; private String dimension;
private Integer type; private Integer type;
@SuppressWarnings("unused")
private String description; private String description;
// TODO: why is this different from the PORTFOLIO constant? This doesn't seem right!
@JsonIgnore @JsonIgnore
public boolean isPortfolio() { public boolean isPortfolio() {
return this.type == 5; return this.type == 5;
@@ -18,17 +18,20 @@
*/ */
package org.apache.fineract.mix.data; package org.apache.fineract.mix.data;
import com.fasterxml.jackson.annotation.JsonInclude; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@Builder
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true) @Accessors(chain = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MixTaxonomyMappingData { public class MixTaxonomyMappingData {
private String identifier; private String identifier;
private String config; private String config;
private String currency;
} }
@@ -0,0 +1,41 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.data;
import java.io.Serial;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MixTaxonomyMappingUpdateRequest implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long id;
private String identifier;
private String config;
private String currency;
}
@@ -21,17 +21,18 @@ package org.apache.fineract.mix.data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@Builder
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class MixTaxonomyRequest implements Serializable { public class MixTaxonomyMappingUpdateResponse implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String identifier; private Long entityId;
private String config;
} }
@@ -0,0 +1,48 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import java.io.Serial;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
@Table("mix_xbrl_namespace")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public class MixReportXBRLNamespace implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id
@Column("id")
private Long id;
@Column("prefix")
private String prefix;
@Column("url")
private String url;
}
@@ -0,0 +1,29 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import java.util.Optional;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
public interface MixReportXBRLNamespaceRepository
extends ListCrudRepository<MixReportXBRLNamespace, Long>, QueryByExampleExecutor<MixReportXBRLNamespace> {
Optional<MixReportXBRLNamespace> findOneByPrefix(String prefix);
}
@@ -0,0 +1,63 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import java.io.Serial;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
@Table("mix_taxonomy")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public final class MixTaxonomy implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id
@Column("id")
private Long id;
@Column("name")
private String name;
@Column("namespace_id")
private Long namespaceId;
@Column("dimension")
private String dimension;
@Column("type")
private Integer type;
@Column("description")
private String description;
// TODO: this is never used, but creates an error on MySQL (tinyint vs boolean mapping)
// @Column("need_mapping")
// private Boolean needMapping;
}
@@ -0,0 +1,53 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import java.io.Serial;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
@Table("mix_taxonomy_mapping")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public final class MixTaxonomyMapping implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id
@Column("id")
private Long id;
@Column("identifier")
private String identifier;
@Column("config")
private String config;
@Column("currency")
private String currency;
}
@@ -18,10 +18,8 @@
*/ */
package org.apache.fineract.mix.domain; package org.apache.fineract.mix.domain;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.query.QueryByExampleExecutor;
public interface MixTaxonomyMappingRepository public interface MixTaxonomyMappingRepository
extends JpaRepository<MixTaxonomyMapping, Long>, JpaSpecificationExecutor<MixTaxonomyMapping> { extends ListCrudRepository<MixTaxonomyMapping, Long>, QueryByExampleExecutor<MixTaxonomyMapping> {}
}
@@ -0,0 +1,28 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import java.util.List;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.query.QueryByExampleExecutor;
public interface MixTaxonomyRepository extends ListCrudRepository<MixTaxonomy, Long>, QueryByExampleExecutor<MixTaxonomy> {
List<MixTaxonomy> findAllByOrderByIdAsc();
}
@@ -20,9 +20,9 @@ package org.apache.fineract.mix.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException; import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class XBRLMappingInvalidException extends AbstractPlatformDomainRuleException { public class MixReportXBRLMappingInvalidException extends AbstractPlatformDomainRuleException {
public XBRLMappingInvalidException(final String msg) { public MixReportXBRLMappingInvalidException(final String msg) {
super("error.msg.xbrl.report.mapping.invalid.id", "Mapping does not exist", msg); super("error.msg.xbrl.report.mapping.invalid.id", "Mapping does not exist", msg);
} }
@@ -0,0 +1,51 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.handler;
import io.github.resilience4j.retry.annotation.Retry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.command.core.Command;
import org.apache.fineract.command.core.CommandHandler;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateResponse;
import org.apache.fineract.mix.service.MixTaxonomyMappingWriteService;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
@RequiredArgsConstructor
public class MixTaxonomyMappingUpdateCommandHandler
implements CommandHandler<MixTaxonomyMappingUpdateRequest, MixTaxonomyMappingUpdateResponse> {
private final MixTaxonomyMappingWriteService writeTaxonomyService;
@Retry(name = "commandMixTaxonomyMappingUpdate", fallbackMethod = "fallback")
@Transactional
@Override
public MixTaxonomyMappingUpdateResponse handle(Command<MixTaxonomyMappingUpdateRequest> command) {
return writeTaxonomyService.updateMapping(command.getPayload());
}
@Override
public MixTaxonomyMappingUpdateResponse fallback(Command<MixTaxonomyMappingUpdateRequest> command, Throwable t) {
return CommandHandler.super.fallback(command, t);
}
}
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.mapping;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.mix.data.MixReportXBRLNamespaceData;
import org.apache.fineract.mix.domain.MixReportXBRLNamespace;
import org.mapstruct.Mapper;
@Mapper(config = MapstructMapperConfig.class)
public interface MixReportXBRLNamespaceMapper {
MixReportXBRLNamespaceData map(MixReportXBRLNamespace source);
}
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.mapping;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.domain.MixTaxonomy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@Mapper(config = MapstructMapperConfig.class)
public interface MixTaxonomyMapper {
@Mapping(ignore = true, target = "namespace")
MixTaxonomyData map(MixTaxonomy source);
}
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.mapping;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.mix.data.MixTaxonomyMappingData;
import org.apache.fineract.mix.domain.MixTaxonomyMapping;
import org.mapstruct.Mapper;
@Mapper(config = MapstructMapperConfig.class)
public interface MixTaxonomyMappingMapper {
MixTaxonomyMappingData map(MixTaxonomyMapping source);
}
@@ -0,0 +1,30 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.mapping;
import org.apache.fineract.infrastructure.core.config.MapstructMapperConfig;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
import org.apache.fineract.mix.domain.MixTaxonomyMapping;
import org.mapstruct.Mapper;
@Mapper(config = MapstructMapperConfig.class)
public interface MixTaxonomyMappingUpdateRequestMapper {
MixTaxonomyMapping map(MixTaxonomyMappingUpdateRequest source);
}
@@ -25,36 +25,40 @@ import java.text.SimpleDateFormat;
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 org.apache.fineract.mix.data.ContextData; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixReportXBRLContextData;
import org.apache.fineract.mix.data.MixReportXBRLData;
import org.apache.fineract.mix.data.MixReportXBRLNamespaceData;
import org.apache.fineract.mix.data.MixTaxonomyData; import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.data.NamespaceData; import org.apache.fineract.mix.exception.MixReportXBRLMappingInvalidException;
import org.apache.fineract.mix.data.XBRLData;
import org.apache.fineract.mix.exception.XBRLMappingInvalidException;
import org.dom4j.Document; import org.dom4j.Document;
import org.dom4j.DocumentHelper; import org.dom4j.DocumentHelper;
import org.dom4j.Element; import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@RequiredArgsConstructor
@Component @Component
public class XBRLBuilder { public class MixReportXBRLBuilder {
// NOTE: see https://www.xbrl.org/taxonomyrecognition/mx_2009-06-19_summary-page.htm
private static final String SCHEME_URL = "http://www.themix.org"; private static final String SCHEME_URL = "http://www.themix.org";
private static final String IDENTIFIER = "000000"; private static final String IDENTIFIER = "000000";
private static final String UNITID_PURE = "Unit1"; private static final String UNITID_PURE = "Unit1";
private static final String UNITID_CUR = "Unit2"; private static final String UNITID_CUR = "Unit2";
@Autowired private final MixReportXBRLNamespaceReadService readNamespaceService;
private NamespaceReadPlatformService readNamespaceService;
public String build(final XBRLData xbrlData) { // TODO: we should do this with JAXB
public String build(final MixReportXBRLData xbrlData) {
return this.build(xbrlData.getResultMap(), xbrlData.getStartDate(), xbrlData.getEndDate(), xbrlData.getCurrency()); return this.build(xbrlData.getResultMap(), xbrlData.getStartDate(), xbrlData.getEndDate(), xbrlData.getCurrency());
} }
public String build(final Map<MixTaxonomyData, BigDecimal> map, final Date startDate, final Date endDate, final String currency) { public String build(final Map<MixTaxonomyData, BigDecimal> map, final Date startDate, final Date endDate, final String currency) {
Integer instantScenarioCounter = 0; Integer instantScenarioCounter = 0;
Integer durationScenarioCounter = 0; Integer durationScenarioCounter = 0;
Map<ContextData, String> contextMap = new HashMap<>(); Map<MixReportXBRLContextData, String> contextMap = new HashMap<>();
final Document doc = DocumentHelper.createDocument(); final Document doc = DocumentHelper.createDocument();
Element root = doc.addElement("xbrl"); Element root = doc.addElement("xbrl");
@@ -79,17 +83,17 @@ public class XBRLBuilder {
private Element addTaxonomy(final Element rootElement, final MixTaxonomyData taxonomy, final BigDecimal value, final Date startDate, private Element addTaxonomy(final Element rootElement, final MixTaxonomyData taxonomy, final BigDecimal value, final Date startDate,
final Date endDate, Integer instantScenarioCounter, Integer durationScenarioCounter, final Date endDate, Integer instantScenarioCounter, Integer durationScenarioCounter,
final Map<ContextData, String> contextMap) { final Map<MixReportXBRLContextData, String> contextMap) {
// throw an error is start / endate is null // throw an error is start / endate is null
if (startDate == null || endDate == null) { if (startDate == null || endDate == null) {
throw new XBRLMappingInvalidException("start date and end date should not be null"); throw new MixReportXBRLMappingInvalidException("start date and end date should not be null");
} }
final String prefix = taxonomy.getNamespace(); final String prefix = taxonomy.getNamespace();
String qname = taxonomy.getName(); String qname = taxonomy.getName();
if (prefix != null && !prefix.isEmpty()) { if (prefix != null && !prefix.isEmpty()) {
final NamespaceData ns = this.readNamespaceService.retrieveNamespaceByPrefix(prefix); final MixReportXBRLNamespaceData ns = this.readNamespaceService.retrieveNamespaceByPrefix(prefix);
if (ns != null) { if (ns != null) {
rootElement.addNamespace(prefix, ns.getUrl()); rootElement.addNamespace(prefix, ns.getUrl());
@@ -102,20 +106,20 @@ public class XBRLBuilder {
final String dimension = taxonomy.getDimension(); final String dimension = taxonomy.getDimension();
final SimpleDateFormat timeFormat = new SimpleDateFormat("MM_dd_yyyy"); final SimpleDateFormat timeFormat = new SimpleDateFormat("MM_dd_yyyy");
ContextData context = null; MixReportXBRLContextData context = null;
if (dimension != null) { if (dimension != null) {
final List<String> dims = Splitter.on(':').splitToList(dimension); final List<String> dims = Splitter.on(':').splitToList(dimension);
if (dims.size() == 2) { if (dims.size() == 2) {
context = new ContextData().setDimensionType(dims.get(0)).setDimension(dims.get(1)).setPeriodType( context = new MixReportXBRLContextData().setDimensionType(dims.get(0)).setDimension(dims.get(1)).setPeriodType(
taxonomy.getType().equals(MixTaxonomyData.BALANCESHEET) || taxonomy.getType().equals(MixTaxonomyData.PORTFOLIO) ? 0 taxonomy.getType().equals(MixTaxonomyData.BALANCE_SHEET) || taxonomy.getType().equals(MixTaxonomyData.PORTFOLIO) ? 0
: 1); : 1);
} }
} }
if (context == null) { if (context == null) {
context = new ContextData().setPeriodType( context = new MixReportXBRLContextData().setPeriodType(
taxonomy.getType().equals(MixTaxonomyData.BALANCESHEET) || taxonomy.getType().equals(MixTaxonomyData.PORTFOLIO) ? 0 taxonomy.getType().equals(MixTaxonomyData.BALANCE_SHEET) || taxonomy.getType().equals(MixTaxonomyData.PORTFOLIO) ? 0
: 1); : 1);
} }
@@ -169,10 +173,11 @@ public class XBRLBuilder {
} }
private void addContexts(final Element root, final Date startDate, final Date endDate, final Map<ContextData, String> contextMap) { private void addContexts(final Element root, final Date startDate, final Date endDate,
final Map<MixReportXBRLContextData, String> contextMap) {
final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
for (final Map.Entry<ContextData, String> entry : contextMap.entrySet()) { for (final Map.Entry<MixReportXBRLContextData, String> entry : contextMap.entrySet()) {
final ContextData context = entry.getKey(); final MixReportXBRLContextData context = entry.getKey();
final Element contextElement = root.addElement("context"); final Element contextElement = root.addElement("context");
contextElement.addAttribute("id", entry.getValue()); contextElement.addAttribute("id", entry.getValue());
contextElement.addElement("entity").addElement("identifier").addAttribute("scheme", SCHEME_URL).addText(IDENTIFIER); contextElement.addElement("entity").addElement("identifier").addAttribute("scheme", SCHEME_URL).addText(IDENTIFIER);
@@ -18,11 +18,9 @@
*/ */
package org.apache.fineract.mix.service; package org.apache.fineract.mix.service;
import org.apache.fineract.mix.data.NamespaceData; import org.apache.fineract.mix.data.MixReportXBRLNamespaceData;
public interface NamespaceReadPlatformService { public interface MixReportXBRLNamespaceReadService {
NamespaceData retrieveNamespaceById(Long id); MixReportXBRLNamespaceData retrieveNamespaceByPrefix(String prefix);
NamespaceData retrieveNamespaceByPrefix(String prefix);
} }
@@ -0,0 +1,40 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixReportXBRLNamespaceData;
import org.apache.fineract.mix.domain.MixReportXBRLNamespaceRepository;
import org.apache.fineract.mix.mapping.MixReportXBRLNamespaceMapper;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class MixReportXBRLNamespaceReadServiceImpl implements MixReportXBRLNamespaceReadService {
private final MixReportXBRLNamespaceRepository repository;
private final MixReportXBRLNamespaceMapper mapper;
@Override
public MixReportXBRLNamespaceData retrieveNamespaceByPrefix(final String prefix) {
return repository.findOneByPrefix(prefix).map(mapper::map).orElse(null);
}
}
@@ -19,10 +19,10 @@
package org.apache.fineract.mix.service; package org.apache.fineract.mix.service;
import java.sql.Date; import java.sql.Date;
import org.apache.fineract.mix.data.XBRLData; import org.apache.fineract.mix.data.MixReportXBRLData;
public interface XBRLResultService { public interface MixReportXBRLResultService {
XBRLData getXBRLResult(Date startDate, Date endDate, String currency); MixReportXBRLData getXBRLResult(Date startDate, Date endDate, String currency);
} }
@@ -31,45 +31,47 @@ import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager; import javax.script.ScriptEngineManager;
import javax.script.ScriptException; import javax.script.ScriptException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixReportXBRLData;
import org.apache.fineract.mix.data.MixTaxonomyData; import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.data.MixTaxonomyMappingData; import org.apache.fineract.mix.data.MixTaxonomyMappingData;
import org.apache.fineract.mix.data.XBRLData; import org.apache.fineract.mix.exception.MixReportXBRLMappingInvalidException;
import org.apache.fineract.mix.exception.XBRLMappingInvalidException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet; import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component
@Slf4j @Slf4j
public class XBRLResultServiceImpl implements XBRLResultService { @Component
public class MixReportXBRLResultServiceImpl implements MixReportXBRLResultService {
private static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript"); private static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript");
private final MixTaxonomyMappingReadPlatformService readTaxonomyMappingService; private final MixTaxonomyMappingReadService readTaxonomyMappingService;
private final MixTaxonomyReadPlatformService readTaxonomyService; private final MixTaxonomyReadService readTaxonomyService;
private final JdbcTemplate jdbcTemplate; private final JdbcTemplate jdbcTemplate;
@Autowired @Autowired
public XBRLResultServiceImpl(final JdbcTemplate jdbcTemplate, final MixTaxonomyMappingReadPlatformService readTaxonomyMappingService, public MixReportXBRLResultServiceImpl(final JdbcTemplate jdbcTemplate, final MixTaxonomyMappingReadService readTaxonomyMappingService,
final MixTaxonomyReadPlatformService readTaxonomyService) { final MixTaxonomyReadService readTaxonomyService) {
this.readTaxonomyMappingService = readTaxonomyMappingService; this.readTaxonomyMappingService = readTaxonomyMappingService;
this.readTaxonomyService = readTaxonomyService; this.readTaxonomyService = readTaxonomyService;
this.jdbcTemplate = jdbcTemplate; this.jdbcTemplate = jdbcTemplate;
} }
@Override @Override
public XBRLData getXBRLResult(final Date startDate, final Date endDate, final String currency) { public MixReportXBRLData getXBRLResult(final Date startDate, final Date endDate, final String currency) {
final HashMap<MixTaxonomyData, BigDecimal> config = retrieveTaxonomyConfig(startDate, endDate); final Map<MixTaxonomyData, BigDecimal> config = retrieveTaxonomyConfig(startDate, endDate);
if (config == null || config.size() == 0) {
throw new XBRLMappingInvalidException("Mapping is empty"); if (config == null || config.isEmpty()) {
throw new MixReportXBRLMappingInvalidException("Mapping is empty");
} }
return new XBRLData().setResultMap(config).setStartDate(startDate).setEndDate(endDate).setCurrency(currency);
return new MixReportXBRLData().setResultMap(config).setStartDate(startDate).setEndDate(endDate).setCurrency(currency);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private HashMap<MixTaxonomyData, BigDecimal> retrieveTaxonomyConfig(final Date startDate, final Date endDate) { private Map<MixTaxonomyData, BigDecimal> retrieveTaxonomyConfig(final Date startDate, final Date endDate) {
final MixTaxonomyMappingData taxonomyMapping = this.readTaxonomyMappingService.retrieveTaxonomyMapping(); final MixTaxonomyMappingData taxonomyMapping = this.readTaxonomyMappingService.retrieveTaxonomyMapping();
if (taxonomyMapping == null) { if (taxonomyMapping == null) {
return null; return null;
@@ -77,7 +79,7 @@ public class XBRLResultServiceImpl implements XBRLResultService {
final String config = taxonomyMapping.getConfig(); final String config = taxonomyMapping.getConfig();
if (config != null) { if (config != null) {
// <taxonomyId, mapping> // <taxonomyId, mapping>
HashMap<String, String> configMap = new HashMap<>(); Map<String, String> configMap = new HashMap<>();
configMap = new Gson().fromJson(config, configMap.getClass()); configMap = new Gson().fromJson(config, configMap.getClass());
if (configMap == null) { if (configMap == null) {
return null; return null;
@@ -96,41 +98,34 @@ public class XBRLResultServiceImpl implements XBRLResultService {
return null; return null;
} }
// TODO: this should at least use prepared statements and not just string concatenate the date objects!
private String getAccountSql(final Date startDate, final Date endDate) { private String getAccountSql(final Date startDate, final Date endDate) {
return "select debits.glcode as 'glcode', debits.name as 'name', coalesce(debits.debitamount,0)-coalesce(credits.creditamount,0)) as 'balance' " return "SELECT debits.glcode AS 'glcode', debits.name AS 'name', COALESCE(debits.debitamount,0)-COALESCE(credits.creditamount,0)) AS 'balance' "
+ "from (select acc_gl_account.gl_code as 'glcode',name,sum(amount) as 'debitamount' " + "FROM (SELECT acc_gl_account.gl_code AS 'glcode',name,SUM(amount) AS 'debitamount' "
+ "from acc_gl_journal_entry,acc_gl_account " + "where acc_gl_account.id = acc_gl_journal_entry.account_id " + "FROM acc_gl_journal_entry,acc_gl_account WHERE acc_gl_account.id = acc_gl_journal_entry.account_id "
+ "and acc_gl_journal_entry.type_enum=2 " + "and acc_gl_journal_entry.entry_date <= " + endDate + "AND acc_gl_journal_entry.type_enum=2 AND acc_gl_journal_entry.entry_date <= " + endDate
+ " and acc_gl_journal_entry.entry_date > " + startDate + " AND acc_gl_journal_entry.entry_date > " + startDate
// "and (acc_gl_journal_entry.office_id=${branch} or //
// ${branch}=1) " + " GROUP BY glcode ORDER BY glcode) debits LEFT OUTER JOIN "
// + + "(SELECT acc_gl_account.gl_code AS 'glcode',name,SUM(amount) AS 'creditamount' "
+ " group by glcode " + "order by glcode) debits " + "LEFT OUTER JOIN " + "FROM acc_gl_journal_entry,acc_gl_account WHERE acc_gl_account.id = acc_gl_journal_entry.account_id "
+ "(select acc_gl_account.gl_code as 'glcode',name,sum(amount) as 'creditamount' " + "AND acc_gl_journal_entry.type_enum=1 AND acc_gl_journal_entry.entry_date <= " + endDate
+ "from acc_gl_journal_entry,acc_gl_account " + "where acc_gl_account.id = acc_gl_journal_entry.account_id " + " AND acc_gl_journal_entry.entry_date > " + startDate
+ "and acc_gl_journal_entry.type_enum=1 " + "and acc_gl_journal_entry.entry_date <= " + endDate //
+ " and acc_gl_journal_entry.entry_date > " + startDate + " GROUP BY glcode ORDER BY glcode) credits ON debits.glcode=credits.glcode UNION "
// "and (acc_gl_journal_entry.office_id=${branch} or + "SELECT credits.glcode AS 'glcode', credits.name AS 'name', COALESCE(debits.debitamount,0)-COALESCE(credits.creditamount,0)) AS 'balance' "
// ${branch}=1) " + "FROM (SELECT acc_gl_account.gl_code AS 'glcode',name,SUM(amount) AS 'debitamount' "
// + + "FROM acc_gl_journal_entry,acc_gl_account WHERE acc_gl_account.id = acc_gl_journal_entry.account_id "
+ " group by glcode " + "order by glcode) credits " + "on debits.glcode=credits.glcode " + "union " + "AND acc_gl_journal_entry.type_enum=2 AND acc_gl_journal_entry.entry_date <= " + endDate
+ "select credits.glcode as 'glcode', credits.name as 'name', coalesce(debits.debitamount,0)-coalesce(credits.creditamount,0)) as 'balance' " + " AND acc_gl_journal_entry.entry_date > " + startDate
+ "from (select acc_gl_account.gl_code as 'glcode',name,sum(amount) as 'debitamount' " //
+ "from acc_gl_journal_entry,acc_gl_account " + "where acc_gl_account.id = acc_gl_journal_entry.account_id " + " GROUP BY glcode ORDER BY glcode) debits RIGHT OUTER JOIN "
+ "and acc_gl_journal_entry.type_enum=2 " + "and acc_gl_journal_entry.entry_date <= " + endDate + "(SELECT acc_gl_account.gl_code AS 'glcode',name,SUM(amount) AS 'creditamount' "
+ " and acc_gl_journal_entry.entry_date > " + startDate + "FROM acc_gl_journal_entry,acc_gl_account WHERE acc_gl_account.id = acc_gl_journal_entry.account_id "
// "and (acc_gl_journal_entry.office_id=${branch} or + "AND acc_gl_journal_entry.type_enum=1 AND acc_gl_journal_entry.entry_date <= " + endDate
// ${branch}=1) " + " AND acc_gl_journal_entry.entry_date > " + startDate
// + //
+ " group by glcode " + "order by glcode) debits " + "RIGHT OUTER JOIN " + " GROUP BY name, glcode ORDER BY glcode) credits ON debits.glcode=credits.glcode;";
+ "(select acc_gl_account.gl_code as 'glcode',name,sum(amount) as 'creditamount' "
+ "from acc_gl_journal_entry,acc_gl_account " + "where acc_gl_account.id = acc_gl_journal_entry.account_id "
+ "and acc_gl_journal_entry.type_enum=1 " + "and acc_gl_journal_entry.entry_date <= " + endDate
+ " and acc_gl_journal_entry.entry_date > " + startDate
// "and (acc_gl_journal_entry.office_id=${branch} or
// ${branch}=1) "
// +
+ " group by name, glcode " + "order by glcode) credits " + "on debits.glcode=credits.glcode;";
} }
private Map<String, BigDecimal> setupBalanceMap(final String sql) { private Map<String, BigDecimal> setupBalanceMap(final String sql) {
@@ -156,6 +151,7 @@ public class XBRLResultServiceImpl implements XBRLResultService {
// evaluate the expression // evaluate the expression
float eval = 0f; float eval = 0f;
try { try {
// TODO: this doesn't work anymore in modern JVMs!!!!
final Number value = (Number) SCRIPT_ENGINE.eval(mappingString); final Number value = (Number) SCRIPT_ENGINE.eval(mappingString);
if (value != null) { if (value != null) {
eval = value.floatValue(); eval = value.floatValue();
@@ -20,7 +20,7 @@ package org.apache.fineract.mix.service;
import org.apache.fineract.mix.data.MixTaxonomyMappingData; import org.apache.fineract.mix.data.MixTaxonomyMappingData;
public interface MixTaxonomyMappingReadPlatformService { public interface MixTaxonomyMappingReadService {
MixTaxonomyMappingData retrieveTaxonomyMapping(); MixTaxonomyMappingData retrieveTaxonomyMapping();
} }
@@ -0,0 +1,40 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixTaxonomyMappingData;
import org.apache.fineract.mix.domain.MixTaxonomyMappingRepository;
import org.apache.fineract.mix.mapping.MixTaxonomyMappingMapper;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class MixTaxonomyMappingReadServiceImpl implements MixTaxonomyMappingReadService {
private final MixTaxonomyMappingRepository repository;
private final MixTaxonomyMappingMapper mapper;
@Override
public MixTaxonomyMappingData retrieveTaxonomyMapping() {
return repository.findAll().stream().findFirst().map(mapper::map).orElse(null);
}
}
@@ -18,10 +18,10 @@
*/ */
package org.apache.fineract.mix.service; package org.apache.fineract.mix.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateResponse;
public interface MixTaxonomyMappingWritePlatformService { public interface MixTaxonomyMappingWriteService {
CommandProcessingResult updateMapping(Long mappingId, JsonCommand command); MixTaxonomyMappingUpdateResponse updateMapping(MixTaxonomyMappingUpdateRequest request);
} }
@@ -0,0 +1,48 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateRequest;
import org.apache.fineract.mix.data.MixTaxonomyMappingUpdateResponse;
import org.apache.fineract.mix.domain.MixTaxonomyMappingRepository;
import org.apache.fineract.mix.mapping.MixTaxonomyMappingUpdateRequestMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@RequiredArgsConstructor
@Service
public class MixTaxonomyMappingWriteServiceImpl implements MixTaxonomyMappingWriteService {
private final MixTaxonomyMappingRepository repository;
private final MixTaxonomyMappingUpdateRequestMapper mapper;
@Transactional
@Override
public MixTaxonomyMappingUpdateResponse updateMapping(@Valid final MixTaxonomyMappingUpdateRequest request) {
final var taxonomyMapping = mapper.map(request);
repository.save(taxonomyMapping);
return MixTaxonomyMappingUpdateResponse.builder().entityId(taxonomyMapping.getId()).build();
}
}
@@ -21,7 +21,7 @@ package org.apache.fineract.mix.service;
import java.util.List; import java.util.List;
import org.apache.fineract.mix.data.MixTaxonomyData; import org.apache.fineract.mix.data.MixTaxonomyData;
public interface MixTaxonomyReadPlatformService { public interface MixTaxonomyReadService {
List<MixTaxonomyData> retrieveAll(); List<MixTaxonomyData> retrieveAll();
@@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.domain.MixTaxonomyRepository;
import org.apache.fineract.mix.mapping.MixTaxonomyMapper;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class MixTaxonomyReadServiceImpl implements MixTaxonomyReadService {
private final MixTaxonomyRepository repository;
private final MixTaxonomyMapper mapper;
@Override
public List<MixTaxonomyData> retrieveAll() {
return repository.findAllByOrderByIdAsc().stream().map(mapper::map).toList();
}
@Override
public MixTaxonomyData retrieveOne(final Long id) {
return repository.findById(id).map(mapper::map).orElse(null);
}
}
+2 -1
View File
@@ -42,6 +42,7 @@ dependencies {
implementation(project(path: ':fineract-loan-origination')) implementation(project(path: ':fineract-loan-origination'))
implementation(project(path: ':fineract-security')) implementation(project(path: ':fineract-security'))
implementation(project(path: ':fineract-working-capital-loan')) implementation(project(path: ':fineract-working-capital-loan'))
implementation(project(path: ':fineract-mix'))
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
@@ -58,7 +59,7 @@ dependencies {
'org.springframework.boot:spring-boot-starter-web', 'org.springframework.boot:spring-boot-starter-web',
'org.springframework.boot:spring-boot-starter-validation', 'org.springframework.boot:spring-boot-starter-validation',
'org.springframework.boot:spring-boot-starter-security', 'org.springframework.boot:spring-boot-starter-security',
"org.springframework.boot:spring-boot-starter-oauth2-authorization-server", 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server',
'org.springframework.boot:spring-boot-starter-cache', 'org.springframework.boot:spring-boot-starter-cache',
'org.springframework.boot:spring-boot-starter-oauth2-resource-server', 'org.springframework.boot:spring-boot-starter-oauth2-resource-server',
'org.springframework.boot:spring-boot-starter-actuator', 'org.springframework.boot:spring-boot-starter-actuator',
@@ -297,6 +297,34 @@ public class SecurityConfig {
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_PAYMENTTYPE") .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_PAYMENTTYPE")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/paymenttypes")) .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/paymenttypes"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_PAYMENTTYPE") .hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_PAYMENTTYPE")
// mix: taxonomy
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/mixtaxonomy/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_MIX_TAXONOMY")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/mixtaxonomy/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "CREATE_MIX_TAXONOMY")
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/mixtaxonomy/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_MIX_TAXONOMY")
.requestMatchers(API_MATCHER.matcher(HttpMethod.DELETE, "/api/*/mixtaxonomy/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_MIX_TAXONOMY")
// mix: mapping
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/mixmapping/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_MIX_MAPPING")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/mixmapping/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "CREATE_MIX_MAPPING")
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/mixmapping/*"))
// TODO: "UPDATE_XBRLMAPPING" is the legacy permission name; we should rename for consistency
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_MIX_MAPPING", "UPDATE_XBRLMAPPING")
.requestMatchers(API_MATCHER.matcher(HttpMethod.DELETE, "/api/*/mixmapping/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_MIX_MAPPING")
// mix: report
.requestMatchers(API_MATCHER.matcher(HttpMethod.GET, "/api/*/mixreport/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_READ, "READ_MIX_REPORT")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/mixreport/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "CREATE_MIX_REPORT")
.requestMatchers(API_MATCHER.matcher(HttpMethod.PUT, "/api/*/mixreport/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "UPDATE_MIX_REPORT")
.requestMatchers(API_MATCHER.matcher(HttpMethod.DELETE, "/api/*/mixreport/*"))
.hasAnyAuthority(ALL_FUNCTIONS, ALL_FUNCTIONS_WRITE, "DELETE_MIX_REPORT")
.requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated() .requestMatchers(API_MATCHER.matcher(HttpMethod.POST, "/api/*/twofactor/validate")).fullyAuthenticated()
.requestMatchers(API_MATCHER.matcher("/api/*/twofactor")).fullyAuthenticated() .requestMatchers(API_MATCHER.matcher("/api/*/twofactor")).fullyAuthenticated()
@@ -1,79 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.data;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class ContextData {
private String dimensionType;
private String dimension;
private Integer periodType;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (this.dimension == null ? 0 : this.dimension.hashCode());
result = prime * result + (this.dimensionType == null ? 0 : this.dimensionType.hashCode());
result = prime * result + (this.periodType == null ? 0 : this.periodType.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ContextData)) {
return false;
}
final ContextData other = (ContextData) obj;
if (this.dimension == null) {
if (other.dimension != null) {
return false;
}
} else if (!this.dimension.equals(other.dimension)) {
return false;
}
if (this.dimensionType == null) {
if (other.dimensionType != null) {
return false;
}
} else if (!this.dimensionType.equals(other.dimensionType)) {
return false;
}
if (this.periodType == null) {
if (other.periodType != null) {
return false;
}
} else if (!this.periodType.equals(other.periodType)) {
return false;
}
return true;
}
}
@@ -1,70 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Entity
@Table(name = "mix_taxonomy_mapping")
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
public final class MixTaxonomyMapping extends AbstractPersistableCustom<Long> {
@Column(name = "identifier")
private String identifier;
@Column(name = "config")
private String config;
@Column(name = "currency")
private String currency;
private MixTaxonomyMapping(final String identifier, final String config, final String currency) {
this.identifier = StringUtils.defaultIfEmpty(identifier, null);
this.config = StringUtils.defaultIfEmpty(config, null);
this.currency = StringUtils.defaultIfEmpty(currency, null);
}
public static MixTaxonomyMapping fromJson(final JsonCommand command) {
final String identifier = command.stringValueOfParameterNamed("identifier");
final String config = command.stringValueOfParameterNamed("config");
final String currency = command.stringValueOfParameterNamed("currency");
return new MixTaxonomyMapping(identifier, config, currency);
}
public void update(final JsonCommand command) {
this.identifier = command.stringValueOfParameterNamed("identifier");
this.config = command.stringValueOfParameterNamed("config");
this.currency = command.stringValueOfParameterNamed("currency");
}
}
@@ -1,47 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.handler;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.mix.service.MixTaxonomyMappingWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "XBRLMAPPING", action = "UPDATE")
public class UpdateTaxonomyMappingCommandHandler implements NewCommandSourceHandler {
private final MixTaxonomyMappingWritePlatformService writeTaxonomyService;
@Autowired
public UpdateTaxonomyMappingCommandHandler(final MixTaxonomyMappingWritePlatformService writeTaxonomyService) {
this.writeTaxonomyService = writeTaxonomyService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writeTaxonomyService.updateMapping(command.entityId(), command);
}
}
@@ -1,60 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.mix.data.MixTaxonomyMappingData;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class MixTaxonomyMappingReadPlatformServiceImpl implements MixTaxonomyMappingReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private static final class TaxonomyMappingMapper implements RowMapper<MixTaxonomyMappingData> {
public String schema() {
return "identifier, config " + "from mix_taxonomy_mapping";
}
@Override
public MixTaxonomyMappingData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final String identifier = rs.getString("identifier");
final String config = rs.getString("config");
return new MixTaxonomyMappingData().setIdentifier(identifier).setConfig(config);
}
}
@Override
public MixTaxonomyMappingData retrieveTaxonomyMapping() {
try {
final TaxonomyMappingMapper rm = new TaxonomyMappingMapper();
final String sqlString = "select " + rm.schema();
return this.jdbcTemplate.queryForObject(sqlString, rm); // NOSONAR
} catch (final EmptyResultDataAccessException e) {
return null;
}
}
}
@@ -1,55 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.mix.domain.MixTaxonomyMapping;
import org.apache.fineract.mix.domain.MixTaxonomyMappingRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
public class MixTaxonomyMappingWritePlatformServiceImpl implements MixTaxonomyMappingWritePlatformService {
private final MixTaxonomyMappingRepository mappingRepository;
@Transactional
@Override
public CommandProcessingResult updateMapping(final Long mappingId, final JsonCommand command) {
try {
MixTaxonomyMapping mapping = this.mappingRepository.findById(mappingId).orElse(null);
if (mapping == null) {
mapping = MixTaxonomyMapping.fromJson(command);
} else {
mapping.update(command);
}
this.mappingRepository.saveAndFlush(mapping);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(mapping.getId()).build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
return CommandProcessingResult.empty();
}
}
}
@@ -1,71 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.apache.fineract.mix.data.MixTaxonomyData;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class MixTaxonomyReadPlatformServiceImpl implements MixTaxonomyReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final MixTaxonomyMapper mixTaxonomyMapper;
public MixTaxonomyReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.mixTaxonomyMapper = new MixTaxonomyMapper();
}
private static final class MixTaxonomyMapper implements RowMapper<MixTaxonomyData> {
public String schema() {
return "tx.id as id, name, dimension, type, description, prefix "
+ "from mix_taxonomy tx left join mix_xbrl_namespace xn on tx.namespace_id=xn.id";
}
@Override
public MixTaxonomyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final long id = rs.getLong("id");
final String name = rs.getString("name");
final String namespace = rs.getString("prefix");
final String dimension = rs.getString("dimension");
final Integer type = rs.getInt("type");
final String desc = rs.getString("description");
return new MixTaxonomyData().setId(id).setName(name).setNamespace(namespace).setDimension(dimension).setType(type)
.setDescription(desc);
}
}
@Override
public List<MixTaxonomyData> retrieveAll() {
final String sql = "select " + this.mixTaxonomyMapper.schema() + " order by id";
return this.jdbcTemplate.query(sql, this.mixTaxonomyMapper); // NOSONAR
}
@Override
public MixTaxonomyData retrieveOne(final Long id) {
final String sql = "select " + this.mixTaxonomyMapper.schema() + " where tx.id = ? ";
return this.jdbcTemplate.queryForObject(sql, this.mixTaxonomyMapper, new Object[] { id }); // NOSONAR
}
}
@@ -1,66 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.fineract.mix.data.NamespaceData;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
public class NamespaceReadPlatformServiceImpl implements NamespaceReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final NamespaceMapper namespaceMapper;
public NamespaceReadPlatformServiceImpl(final JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.namespaceMapper = new NamespaceMapper();
}
private static final class NamespaceMapper implements RowMapper<NamespaceData> {
public String schema() {
return "select id, prefix, url " + "from mix_xbrl_namespace";
}
@Override
public NamespaceData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final long id = rs.getLong("id");
final String prefix = rs.getString("prefix");
final String url = rs.getString("url");
return new NamespaceData().setId(id).setPrefix(prefix).setUrl(url);
}
}
@Override
public NamespaceData retrieveNamespaceById(final Long id) {
final String sql = this.namespaceMapper.schema() + " where id= ? ";
return this.jdbcTemplate.queryForObject(sql, this.namespaceMapper, new Object[] { id }); // NOSONAR
}
@Override
public NamespaceData retrieveNamespaceByPrefix(final String prefix) {
final String sql = this.namespaceMapper.schema() + " where prefix = ? ";
return this.jdbcTemplate.queryForObject(sql, this.namespaceMapper, new Object[] { prefix }); // NOSONAR
}
}
@@ -1,62 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.mix.starter;
import org.apache.fineract.mix.domain.MixTaxonomyMappingRepository;
import org.apache.fineract.mix.service.MixTaxonomyMappingReadPlatformService;
import org.apache.fineract.mix.service.MixTaxonomyMappingReadPlatformServiceImpl;
import org.apache.fineract.mix.service.MixTaxonomyMappingWritePlatformService;
import org.apache.fineract.mix.service.MixTaxonomyMappingWritePlatformServiceImpl;
import org.apache.fineract.mix.service.MixTaxonomyReadPlatformService;
import org.apache.fineract.mix.service.MixTaxonomyReadPlatformServiceImpl;
import org.apache.fineract.mix.service.NamespaceReadPlatformService;
import org.apache.fineract.mix.service.NamespaceReadPlatformServiceImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class MixConfiguration {
@Bean
@ConditionalOnMissingBean(MixTaxonomyMappingReadPlatformService.class)
public MixTaxonomyMappingReadPlatformService mixTaxonomyMappingReadPlatformService(JdbcTemplate jdbcTemplate) {
return new MixTaxonomyMappingReadPlatformServiceImpl(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean(MixTaxonomyMappingWritePlatformService.class)
public MixTaxonomyMappingWritePlatformService mixTaxonomyMappingWritePlatformService(MixTaxonomyMappingRepository mappingRepository) {
return new MixTaxonomyMappingWritePlatformServiceImpl(mappingRepository);
}
@Bean
@ConditionalOnMissingBean(MixTaxonomyReadPlatformService.class)
public MixTaxonomyReadPlatformService mixTaxonomyReadPlatformService(JdbcTemplate jdbcTemplate) {
return new MixTaxonomyReadPlatformServiceImpl(jdbcTemplate);
}
@Bean
@ConditionalOnMissingBean(NamespaceReadPlatformService.class)
public NamespaceReadPlatformService namespaceReadPlatformService(JdbcTemplate jdbcTemplate) {
return new NamespaceReadPlatformServiceImpl(jdbcTemplate);
}
}
@@ -597,6 +597,14 @@ resilience4j.retry.instances.commandPaymentTypeDelete.enable-exponential-backoff
resilience4j.retry.instances.commandPaymentTypeDelete.exponential-backoff-multiplier=${FINERACT_COMMAND_PAYMENT_TYPE_DELETE_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2} resilience4j.retry.instances.commandPaymentTypeDelete.exponential-backoff-multiplier=${FINERACT_COMMAND_PAYMENT_TYPE_DELETE_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2}
resilience4j.retry.instances.commandPaymentTypeDelete.retryExceptions=${FINERACT_COMMAND_PAYMENT_TYPE_DELETE_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException} resilience4j.retry.instances.commandPaymentTypeDelete.retryExceptions=${FINERACT_COMMAND_PAYMENT_TYPE_DELETE_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException}
# mix taxonomy
resilience4j.retry.instances.commandMixTaxonomyMappingUpdate.max-attempts=${FINERACT_COMMAND_MIX_TAXONOMY_MAPPING_UPDATE_RETRY_MAX_ATTEMPTS:3}
resilience4j.retry.instances.commandMixTaxonomyMappingUpdate.wait-duration=${FINERACT_COMMAND_MIX_TAXONOMY_MAPPING_UPDATE_RETRY_WAIT_DURATION:1s}
resilience4j.retry.instances.commandMixTaxonomyMappingUpdate.enable-exponential-backoff=${FINERACT_COMMAND_MIX_TAXONOMY_MAPPING_UPDATE_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true}
resilience4j.retry.instances.commandMixTaxonomyMappingUpdate.exponential-backoff-multiplier=${FINERACT_COMMAND_MIX_TAXONOMY_MAPPING_UPDATE_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2}
resilience4j.retry.instances.commandMixTaxonomyMappingUpdate.retryExceptions=${FINERACT_COMMAND_MIX_TAXONOMY_MAPPING_UPDATE_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException}
# command # command
fineract.command.enabled=true fineract.command.enabled=true
@@ -279,9 +279,6 @@
<!-- AdHocQuery --> <!-- AdHocQuery -->
<class>org.apache.fineract.adhocquery.domain.AdHoc</class> <class>org.apache.fineract.adhocquery.domain.AdHoc</class>
<!-- Mix -->
<class>org.apache.fineract.mix.domain.MixTaxonomyMapping</class>
<!-- COB (Chunk of Business) --> <!-- COB (Chunk of Business) -->
<class>org.apache.fineract.cob.domain.LoanAccountLock</class> <class>org.apache.fineract.cob.domain.LoanAccountLock</class>
<class>org.apache.fineract.cob.domain.BatchBusinessStep</class> <class>org.apache.fineract.cob.domain.BatchBusinessStep</class>
@@ -29,18 +29,18 @@ import java.nio.charset.StandardCharsets;
import java.sql.Date; import java.sql.Date;
import java.util.HashMap; import java.util.HashMap;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.fineract.mix.data.MixReportXBRLNamespaceData;
import org.apache.fineract.mix.data.MixTaxonomyData; import org.apache.fineract.mix.data.MixTaxonomyData;
import org.apache.fineract.mix.data.NamespaceData; import org.apache.fineract.mix.service.MixReportXBRLBuilder;
import org.apache.fineract.mix.service.NamespaceReadPlatformServiceImpl; import org.apache.fineract.mix.service.MixReportXBRLNamespaceReadServiceImpl;
import org.apache.fineract.mix.service.XBRLBuilder;
import org.apache.fineract.template.service.TemplateServiceStepDefinitions; import org.apache.fineract.template.service.TemplateServiceStepDefinitions;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
public class MixXbrlBuilderStepDefinitions implements En { public class MixXbrlBuilderStepDefinitions implements En {
private NamespaceReadPlatformServiceImpl readNamespaceService; private MixReportXBRLNamespaceReadServiceImpl readNamespaceService;
private final XBRLBuilder xbrlBuilder = new XBRLBuilder(); private MixReportXBRLBuilder xbrlBuilder;
private Date start; private Date start;
@@ -55,9 +55,11 @@ public class MixXbrlBuilderStepDefinitions implements En {
public MixXbrlBuilderStepDefinitions() { public MixXbrlBuilderStepDefinitions() {
Given("/^The XBRL input parameters start date (.*), end date (.*), currency (.*), taxonomy (.*) and sample (.*)$/", Given("/^The XBRL input parameters start date (.*), end date (.*), currency (.*), taxonomy (.*) and sample (.*)$/",
(String start, String end, String currency, String taxonomy, String sample) -> { (String start, String end, String currency, String taxonomy, String sample) -> {
readNamespaceService = mock(NamespaceReadPlatformServiceImpl.class); readNamespaceService = mock(MixReportXBRLNamespaceReadServiceImpl.class);
lenient().when(this.readNamespaceService.retrieveNamespaceByPrefix(ArgumentMatchers.anyString())) lenient().when(this.readNamespaceService.retrieveNamespaceByPrefix(ArgumentMatchers.anyString()))
.thenReturn(new NamespaceData().setId(1L).setPrefix("mockedprefix").setUrl("mockedurl")); .thenReturn(new MixReportXBRLNamespaceData().setId(1L).setPrefix("mockedprefix").setUrl("mockedurl"));
xbrlBuilder = new MixReportXBRLBuilder(readNamespaceService);
this.start = Date.valueOf(start); this.start = Date.valueOf(start);
this.end = Date.valueOf(end); this.end = Date.valueOf(end);
@@ -23,13 +23,13 @@ import static org.mockito.Mockito.mock;
import io.cucumber.java8.En; import io.cucumber.java8.En;
import java.util.List; import java.util.List;
import org.apache.fineract.mix.service.XBRLResultServiceImpl; import org.apache.fineract.mix.service.MixReportXBRLResultServiceImpl;
import org.assertj.core.util.Arrays; import org.assertj.core.util.Arrays;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
public class MixXbrlTaxonomyStepDefinitions implements En { public class MixXbrlTaxonomyStepDefinitions implements En {
private XBRLResultServiceImpl readService; private MixReportXBRLResultServiceImpl readService;
private String template; private String template;
@@ -37,7 +37,7 @@ public class MixXbrlTaxonomyStepDefinitions implements En {
public MixXbrlTaxonomyStepDefinitions() { public MixXbrlTaxonomyStepDefinitions() {
Given("/^A XBRL template (.*)$/", (String template) -> { Given("/^A XBRL template (.*)$/", (String template) -> {
this.readService = new XBRLResultServiceImpl(mock(JdbcTemplate.class), null, null); this.readService = new MixReportXBRLResultServiceImpl(mock(JdbcTemplate.class), null, null);
this.template = template; this.template = template;
}); });
+1
View File
@@ -83,6 +83,7 @@ dependencies {
implementation project(':fineract-investor') implementation project(':fineract-investor')
implementation project(':fineract-branch') implementation project(':fineract-branch')
implementation project(':fineract-document') implementation project(':fineract-document')
implementation project(':fineract-mix')
implementation project(':fineract-charge') implementation project(':fineract-charge')
implementation project(':fineract-rates') implementation project(':fineract-rates')
implementation project(':fineract-loan') implementation project(':fineract-loan')
+1
View File
@@ -64,6 +64,7 @@ include ':fineract-loan-origination'
include ':fineract-loan' include ':fineract-loan'
include ':fineract-savings' include ':fineract-savings'
include ':fineract-report' include ':fineract-report'
include ':fineract-mix'
include ':fineract-war' include ':fineract-war'
include ':integration-tests' include ':integration-tests'
include ':twofactor-tests' include ':twofactor-tests'