Merge branch 'yanna' into develop

This commit is contained in:
Keith Woodlock
2013-09-09 10:01:07 +01:00
44 changed files with 1544 additions and 53 deletions
@@ -0,0 +1,48 @@
package org.mifosplatform.integrationtests;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
import org.mifosplatform.integrationtests.common.Utils;
import org.mifosplatform.integrationtests.common.xbrl.XBRLIntegrationTestHelper;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class XBRLIntegrationTest {
private RequestSpecification requestSpec;
private ResponseSpecification responseSpec;
private XBRLIntegrationTestHelper xbrlHelper;
@Before
public void setUp() throws Exception {
Utils.initializeRESTAssured();
requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
}
@Test
public void shouldRetrieveTaxonomyList() {
xbrlHelper = new XBRLIntegrationTestHelper(requestSpec, responseSpec);
ArrayList<HashMap> taxonomyList = xbrlHelper.getTaxonomyList();
this.verifyTaxonomyList(taxonomyList);
}
private void verifyTaxonomyList(ArrayList<HashMap> taxonomyList) {
System.out.println("--------------------VERIFYING TAXONOMY LIST--------------------------");
assertEquals("Checking for the 1st taxonomy", "AdministrativeExpense", taxonomyList.get(0).get("name"));
}
}
@@ -0,0 +1,36 @@
package org.mifosplatform.integrationtests.common.xbrl;
import java.util.ArrayList;
import java.util.HashMap;
import org.mifosplatform.integrationtests.common.Utils;
import com.jayway.restassured.specification.RequestSpecification;
import com.jayway.restassured.specification.ResponseSpecification;
@SuppressWarnings("rawtypes")
public class XBRLIntegrationTestHelper {
private final RequestSpecification requestSpec;
private final ResponseSpecification responseSpec;
private static final String GET_TAXONOMY_LIST_URL = "/mifosng-provider/api/v1/mixtaxonomy?tenantIdentifier=default";
private static final String TAXONOMY_MAPPING_URL = "/mifosng-provider/api/v1/mixmapping?tenantIdentifier=default";
public XBRLIntegrationTestHelper(RequestSpecification requestSpec, ResponseSpecification responseSpec) {
this.requestSpec = requestSpec;
this.responseSpec = responseSpec;
}
public ArrayList getTaxonomyList() {
ArrayList response = Utils.performServerGet(requestSpec, responseSpec, GET_TAXONOMY_LIST_URL, "");
return response;
}
public HashMap getTaxonomyMapping() {
HashMap response = Utils.performServerGet(requestSpec, responseSpec, TAXONOMY_MAPPING_URL, "config");
return response;
}
}
@@ -5,14 +5,17 @@
*/
package org.mifosplatform.accounting.producttoaccountmapping.serialization;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
@@ -24,8 +27,6 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.*;
import com.google.gson.JsonElement;
/**
@@ -5,16 +5,18 @@
*/
package org.mifosplatform.accounting.producttoaccountmapping.service;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.producttoaccountmapping.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
@@ -22,8 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.*;
import com.google.gson.JsonElement;
@Service
@@ -8,9 +8,9 @@ package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.util.HashMap;
import java.util.Map;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_SAVINGS;
import org.mifosplatform.accounting.common.AccountingConstants.SAVINGS_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.common.AccountingRuleType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
@@ -288,7 +288,7 @@ public class CommandWrapper {
public boolean isGroupActivation() {
return this.actionName.equalsIgnoreCase("ACTIVATE") && this.entityName.equalsIgnoreCase("GROUP");
}
public boolean isGroupClose() {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("GROUP");
}
@@ -300,7 +300,7 @@ public class CommandWrapper {
public boolean isCenterClose() {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CENTER");
}
public boolean isClientIdentifierResource() {
return this.entityName.equals("CLIENTIDENTIFIER");
}
@@ -421,11 +421,11 @@ public class CommandWrapper {
public boolean isUnassignStaff() {
return this.actionName.equalsIgnoreCase("UNASSIGNSTAFF") && this.entityName.equalsIgnoreCase("GROUP");
}
public boolean isAssignStaff() {
return this.actionName.equalsIgnoreCase("ASSIGNSTAFF");
}
public String commandName() {
return this.actionName + "_" + this.entityName;
}
@@ -465,7 +465,7 @@ public class CommandWrapper {
public boolean isSavingsAccountDeposit() {
return this.actionName.equalsIgnoreCase("DEPOSIT") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
public boolean isSavingsAccountClose() {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("SAVINGSACCOUNT");
}
@@ -540,6 +540,10 @@ public class CommandWrapper {
return this.actionName.equalsIgnoreCase("DISASSOCIATECLIENTS");
}
public boolean isXBRLMappingResource() {
return this.entityName.equalsIgnoreCase("XBRLMAPPING");
}
public boolean isHolidayResource() {
return this.entityName.equalsIgnoreCase("HOLIDAY");
}
@@ -563,7 +567,7 @@ public class CommandWrapper {
public boolean isUpdateGroupRole() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("UPDATEROLE");
}
public boolean isTransferClientsBetweenGroups() {
return this.entityName.equalsIgnoreCase("GROUP") && this.actionName.equalsIgnoreCase("TRANSFERCLIENTS");
}
@@ -575,19 +579,19 @@ public class CommandWrapper {
public boolean isClientClose() {
return this.actionName.equalsIgnoreCase("CLOSE") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isProposeClientTransfer() {
return this.actionName.equalsIgnoreCase("PROPOSETRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isWithdrawClientTransfer() {
return this.actionName.equalsIgnoreCase("WITHDRAWTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isAcceptClientTransfer() {
return this.actionName.equalsIgnoreCase("ACCEPTTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
public boolean isRejectClientTransfer() {
return this.actionName.equalsIgnoreCase("REJECTTRANSFER") && this.entityName.equalsIgnoreCase("CLIENT");
}
@@ -595,7 +599,7 @@ public class CommandWrapper {
public boolean isProductMixResource() {
return this.entityName.equalsIgnoreCase("PRODUCTMIX");
}
public boolean isSchedulerResource() {
return this.entityName.equalsIgnoreCase("SCHEDULER");
}
@@ -603,12 +607,12 @@ public class CommandWrapper {
public boolean isAccountTransferResource() {
return this.entityName.equalsIgnoreCase("ACCOUNTTRANSFER");
}
public boolean isMeetingResource() {
return this.entityName.equalsIgnoreCase("MEETING");
}
public boolean isSaveOrUpdateAttendance(){
public boolean isSaveOrUpdateAttendance() {
return this.actionName.equalsIgnoreCase("SAVEORUPDATEATTENDANCE");
}
}
@@ -1195,6 +1195,14 @@ public class CommandWrapperBuilder {
this.href = "/accountingrules/" + accountingRuleId;
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() {
this.actionName = "CREATE";
@@ -517,6 +517,12 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isXBRLMappingResource()) {
if (wrapper.isUpdate()) {
handler = applicationContext.getBean("updateTaxonomyMappingCommandHandler", NewCommandSourceHandler.class);
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
} else if (wrapper.isHolidayResource()) {
if (wrapper.isCreate()) {
@@ -551,6 +557,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
throw new UnsupportedCommandException(wrapper.commandName());
}
} else {
throw new UnsupportedCommandException(wrapper.commandName());
}
@@ -5,9 +5,27 @@
*/
package org.mifosplatform.infrastructure.documentmanagement.api;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataParam;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
@@ -22,17 +40,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.sun.jersey.core.header.FormDataContentDisposition;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataParam;
@Path("{entityType}/{entityId}/documents")
@Component
@@ -1,13 +1,13 @@
package org.mifosplatform.infrastructure.documentmanagement.data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FileData {
private static final Logger logger = LoggerFactory.getLogger(FileData.class);
@@ -5,11 +5,11 @@
*/
package org.mifosplatform.infrastructure.documentmanagement.service;
import java.util.Collection;
import org.mifosplatform.infrastructure.documentmanagement.data.DocumentData;
import org.mifosplatform.infrastructure.documentmanagement.data.FileData;
import java.util.Collection;
public interface DocumentReadPlatformService {
Collection<DocumentData> retrieveAllDocuments(String entityType, Long entityId);
@@ -5,6 +5,10 @@
*/
package org.mifosplatform.infrastructure.documentmanagement.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository;
@@ -19,10 +23,6 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
@Service
public class DocumentReadPlatformServiceImpl implements DocumentReadPlatformService {
@@ -5,6 +5,28 @@
*/
package org.mifosplatform.portfolio.calendar.api;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -25,12 +47,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.util.*;
@Path("/{entityType}/{entityId}/calendars")
@Component
@Scope("singleton")
@@ -11,6 +11,8 @@ import org.mifosplatform.portfolio.calendar.data.CalendarData;
import java.util.Collection;
import java.util.List;
import org.mifosplatform.portfolio.calendar.data.CalendarData;
public interface CalendarReadPlatformService {
CalendarData retrieveCalendar(final Long calendarId, Long entityId, Integer entityTypeId);
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.portfolio.savings.domain;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.SAVINGS_PRODUCT_RESOURCE_NAME;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.annualFeeAmountParamName;
@@ -5,6 +5,8 @@
*/
package org.mifosplatform.portfolio.savings.service;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
import java.util.Map;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
@@ -25,8 +27,6 @@ import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.mifosplatform.portfolio.savings.SavingsApiConstants.accountingRuleParamName;
@Service
public class SavingsProductWritePlatformServiceJpaRepositoryImpl implements SavingsProductWritePlatformService {
@@ -0,0 +1,80 @@
package org.mifosplatform.xbrl.mapping.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.xbrl.mapping.data.TaxonomyMappingData;
import org.mifosplatform.xbrl.mapping.service.ReadTaxonomyMappingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/mixmapping")
@Component
@Scope("singleton")
public class TaxonomyMappingApiResource {
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("identifier", "config"));
private final PlatformSecurityContext context;
private final ToApiJsonSerializer<TaxonomyMappingData> toApiJsonSerializer;
private final ReadTaxonomyMappingService readTaxonomyMappingService;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@Autowired
public TaxonomyMappingApiResource(PlatformSecurityContext context, ToApiJsonSerializer<TaxonomyMappingData> toApiJsonSerializer,
ReadTaxonomyMappingService readTaxonomyMappingService,
PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
ApiRequestParameterHelper apiRequestParameterHelper) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.readTaxonomyMappingService = readTaxonomyMappingService;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTaxonomyMapping(@Context final UriInfo uriInfo) {
context.authenticatedUser();
TaxonomyMappingData mappingData = this.readTaxonomyMappingService.retrieveTaxonomyMapping();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, mappingData, RESPONSE_DATA_PARAMETERS);
}
@PUT
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateTaxonomyMapping(final String jsonRequestBody) {
// TODO support multiple configuration file loading
final Long mappingId = (long) 1;
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateTaxonomyMapping(mappingId).withJson(jsonRequestBody)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.toApiJsonSerializer.serialize(result);
}
}
@@ -0,0 +1,21 @@
package org.mifosplatform.xbrl.mapping.data;
public class TaxonomyMappingData {
private final String identifier;
private final String config;
public TaxonomyMappingData(final String identifier, final String config) {
this.identifier = identifier;
this.config = config;
}
public String getIdentifier() {
return this.identifier;
}
public String getConfig() {
return this.config;
}
}
@@ -0,0 +1,49 @@
package org.mifosplatform.xbrl.mapping.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "mix_taxonomy_mapping")
public class TaxonomyMapping extends AbstractPersistable<Long> {
@Column(name = "identifier")
private String identifier;
@Column(name = "config")
private String config;
@Column(name = "currency")
private String currency;
protected TaxonomyMapping() {
// default
}
private TaxonomyMapping(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 TaxonomyMapping fromJson(final JsonCommand command) {
final String identifier = command.stringValueOfParameterNamed("identifier");
final String config = command.stringValueOfParameterNamed("config");
final String currency = command.stringValueOfParameterNamed("currency");
return new TaxonomyMapping(identifier, config, currency);
}
public void update(final JsonCommand command) {
this.identifier = command.stringValueOfParameterNamed("identifier");
this.config = command.stringValueOfParameterNamed("config");
this.currency = command.stringValueOfParameterNamed("currency");
}
}
@@ -0,0 +1,8 @@
package org.mifosplatform.xbrl.mapping.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface TaxonomyMappingRepository extends JpaRepository<TaxonomyMapping, Long>, JpaSpecificationExecutor<TaxonomyMapping> {
}
@@ -0,0 +1,27 @@
package org.mifosplatform.xbrl.mapping.handler;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.xbrl.mapping.service.WriteTaxonomyMappingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UpdateTaxonomyMappingCommandHandler implements NewCommandSourceHandler {
private final WriteTaxonomyMappingService writeTaxonomyService;
@Autowired
public UpdateTaxonomyMappingCommandHandler(WriteTaxonomyMappingService writeTaxonomyService) {
this.writeTaxonomyService = writeTaxonomyService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(JsonCommand command) {
return this.writeTaxonomyService.updateMapping(command.entityId(), command);
}
}
@@ -0,0 +1,8 @@
package org.mifosplatform.xbrl.mapping.service;
import org.mifosplatform.xbrl.mapping.data.TaxonomyMappingData;
public interface ReadTaxonomyMappingService {
TaxonomyMappingData retrieveTaxonomyMapping();
}
@@ -0,0 +1,50 @@
package org.mifosplatform.xbrl.mapping.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.xbrl.mapping.data.TaxonomyMappingData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ReadTaxonomyMappingServiceImpl implements ReadTaxonomyMappingService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public ReadTaxonomyMappingServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class TaxonomyMappingMapper implements RowMapper<TaxonomyMappingData> {
public String schema() {
return "identifier, config " + "from mix_taxonomy_mapping";
}
@Override
public TaxonomyMappingData mapRow(ResultSet rs, @SuppressWarnings("unused") int rowNum) throws SQLException {
final String identifier = rs.getString("identifier");
final String config = rs.getString("config");
return new TaxonomyMappingData(identifier, config);
}
}
@Override
public TaxonomyMappingData retrieveTaxonomyMapping() {
try {
final TaxonomyMappingMapper rm = new TaxonomyMappingMapper();
String sqlString = "select " + rm.schema();
return this.jdbcTemplate.queryForObject(sqlString, rm);
} catch (final EmptyResultDataAccessException e) {
return null;
}
}
}
@@ -0,0 +1,9 @@
package org.mifosplatform.xbrl.mapping.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface WriteTaxonomyMappingService {
CommandProcessingResult updateMapping(Long mappingId, JsonCommand command);
}
@@ -0,0 +1,48 @@
package org.mifosplatform.xbrl.mapping.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.xbrl.mapping.domain.TaxonomyMapping;
import org.mifosplatform.xbrl.mapping.domain.TaxonomyMappingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class WriteTaxonomyMappingServiceImpl implements WriteTaxonomyMappingService {
private final PlatformSecurityContext context;
private final TaxonomyMappingRepository mappingRepository;
@Autowired
public WriteTaxonomyMappingServiceImpl(final PlatformSecurityContext context, final TaxonomyMappingRepository mappingRepository) {
this.context = context;
this.mappingRepository = mappingRepository;
}
@Transactional
@Override
public CommandProcessingResult updateMapping(final Long mappingId, final JsonCommand command) {
try {
context.authenticatedUser();
TaxonomyMapping mapping = this.mappingRepository.findOne(mappingId);
if (mapping == null) {
mapping = TaxonomyMapping.fromJson(command);
} else {
mapping.update(command);
}
this.mappingRepository.saveAndFlush(mapping);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(mapping.getId()).build();
} catch (DataIntegrityViolationException dve) {
return CommandProcessingResult.empty();
}
}
}
@@ -0,0 +1,45 @@
package org.mifosplatform.xbrl.report.api;
import java.sql.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.xbrl.report.service.XBRLBuilder;
import org.mifosplatform.xbrl.report.service.XBRLResultService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/mixreport")
@Component
@Scope("singleton")
public class XBRLReportApiResource {
private final PlatformSecurityContext context;
private final XBRLResultService xbrlResultService;
@Autowired
private XBRLBuilder xbrlBuilder;
@Autowired
public XBRLReportApiResource(final PlatformSecurityContext context, final XBRLResultService xbrlResultService) {
this.context = context;
this.xbrlResultService = xbrlResultService;
}
@GET
@Produces({ MediaType.APPLICATION_XML })
public String retrieveXBRLReport(@QueryParam("startDate") final Date startDate, @QueryParam("endDate") final Date endDate,
@QueryParam("currency") final String currency) {
context.authenticatedUser();
String xbrl = xbrlBuilder.build(this.xbrlResultService.getXBRLResult(startDate, endDate, currency));
return xbrl;
}
}
@@ -0,0 +1,57 @@
package org.mifosplatform.xbrl.report.data;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
public class ContextData {
private final String dimensionType;
private final String dimension;
private final Integer periodType;
public ContextData(String dimensionType, String dimension, Integer taxonomyType) {
this.dimensionType = dimensionType;
this.dimension = dimension;
this.periodType = (taxonomyType == TaxonomyData.BALANCESHEET || taxonomyType == TaxonomyData.PORTFOLIO) ? 0 : 1;
}
public String getDimensionType() {
return this.dimensionType;
}
public String getDimension() {
return this.dimension;
}
public Integer getPeriodType() {
return this.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(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
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;
}
}
@@ -0,0 +1,28 @@
package org.mifosplatform.xbrl.report.data;
public class NamespaceData {
private final Long id;
private final String prefix;
private final String url;
public NamespaceData(Long id, String prefix, String url) {
this.id = id;
this.prefix = prefix;
this.url = url;
}
public Long getId() {
return this.id;
}
public String getPrefix() {
return this.prefix;
}
public String getUrl() {
return this.url;
}
}
@@ -0,0 +1,11 @@
package org.mifosplatform.xbrl.report.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class XBRLMappingInvalidException extends AbstractPlatformDomainRuleException {
public XBRLMappingInvalidException(String msg) {
super("error.msg.xbrl.report.mapping.invalid.id", "Mapping does not exist", msg);
}
}
@@ -0,0 +1,10 @@
package org.mifosplatform.xbrl.report.service;
import org.mifosplatform.xbrl.report.data.NamespaceData;
public interface ReadNamespaceService {
NamespaceData retrieveNamespaceById(Long id);
NamespaceData retrieveNamespaceByPrefix(String prefix);
}
@@ -0,0 +1,55 @@
package org.mifosplatform.xbrl.report.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.xbrl.report.data.NamespaceData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ReadNamespaceServiceImpl implements ReadNamespaceService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public ReadNamespaceServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class NamespaceMapper implements RowMapper<NamespaceData> {
public String schema() {
return "select id, prefix, url " + "from mix_xbrl_namespace";
}
@Override
public NamespaceData mapRow(ResultSet rs, @SuppressWarnings("unused") 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(id, prefix, url);
}
}
@Override
public NamespaceData retrieveNamespaceById(Long id) {
final NamespaceMapper mapper = new NamespaceMapper();
String sql = mapper.schema() + " where id=" + id;
return this.jdbcTemplate.queryForObject(sql, mapper);
}
@Override
public NamespaceData retrieveNamespaceByPrefix(String prefix) {
final NamespaceMapper mapper = new NamespaceMapper();
String sql = mapper.schema() + " where prefix='" + prefix + "'";
return this.jdbcTemplate.queryForObject(sql, mapper);
}
}
@@ -0,0 +1,191 @@
package org.mifosplatform.xbrl.report.service;
import java.math.BigDecimal;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.mifosplatform.xbrl.report.data.ContextData;
import org.mifosplatform.xbrl.report.data.NamespaceData;
import org.mifosplatform.xbrl.report.exception.XBRLMappingInvalidException;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
import org.mifosplatform.xbrl.taxonomy.data.XBRLData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class XBRLBuilder {
private static final String SCHEME_URL = "http://www.themix.org";
private static final String IDENTIFIER = "000000";
private static final String UNITID_PURE = "Unit1";
private static final String UNITID_CUR = "Unit2";
private Element root;
private HashMap<ContextData, String> contextMap;
Date startDate;
Date endDate;
private Integer instantScenarioCounter = 1;
private Integer durationScenarioCounter = 1;
@Autowired
private ReadNamespaceService readNamespaceService;
public String build(XBRLData xbrlData) {
return this.build(xbrlData.getResultMap(), xbrlData.getStartDate(), xbrlData.getEndDate(), xbrlData.getCurrency());
}
public String build(Map<TaxonomyData, BigDecimal> map, Date startDate, Date endDate, String currency) {
instantScenarioCounter = 1;
durationScenarioCounter = 1;
contextMap = new HashMap<ContextData, String>();
Document doc = DocumentHelper.createDocument();
root = doc.addElement("xbrl");
root.addElement("schemaRef").addNamespace("link",
"http://www.themix.org/sites/default/files/Taxonomy2010/dct/dc-all_2010-08-31.xsd");
this.startDate = startDate;
this.endDate = endDate;
for (Entry<TaxonomyData, BigDecimal> entry : map.entrySet()) {
TaxonomyData taxonomy = entry.getKey();
BigDecimal value = entry.getValue();
this.addTaxonomy(root, taxonomy, value);
}
this.addContexts();
this.addCurrencyUnit(currency);
this.addNumberUnit();
doc.setXMLEncoding("UTF-8");
return doc.asXML();
}
Element addTaxonomy(Element rootElement, TaxonomyData taxonomy, BigDecimal value) {
// throw an error is start / endate is null
if (startDate == null || endDate == null) { throw new XBRLMappingInvalidException("start date and end date should not be null"); }
String prefix = taxonomy.getNamespace();
String qname = taxonomy.getName();
if (prefix != null && (!prefix.isEmpty())) {
NamespaceData ns = readNamespaceService.retrieveNamespaceByPrefix(prefix);
if (ns != null) {
root.addNamespace(prefix, ns.getUrl());
}
qname = prefix + ":" + taxonomy.getName();
}
Element xmlElement = rootElement.addElement(qname);
String dimension = taxonomy.getDimension();
SimpleDateFormat timeFormat = new SimpleDateFormat("MM_dd_yyyy");
ContextData context = null;
if (dimension != null) {
String[] dims = dimension.split(":");
if (dims.length == 2) {
context = new ContextData(dims[0], dims[1], taxonomy.getType());
if (contextMap.containsKey(context)) {
} else {
}
}
}
if (context == null) {
context = new ContextData(null, null, taxonomy.getType());
}
if (!contextMap.containsKey(context)) {
String startDateStr = timeFormat.format(startDate);
String endDateStr = timeFormat.format(endDate);
String contextRefID = (context.getPeriodType() == 0) ? ("As_Of_" + endDateStr + (instantScenarioCounter++)) : ("Duration_"
+ startDateStr + "_To_" + endDateStr + (durationScenarioCounter++));
contextMap.put(context, contextRefID);
}
xmlElement.addAttribute("contextRef", contextMap.get(context));
xmlElement.addAttribute("unitRef", getUnitRef(taxonomy));
xmlElement.addAttribute("decimals", getNumberOfDecimalPlaces(value).toString());
// add the child
xmlElement.addText(value.toPlainString());
return xmlElement;
}
private String getUnitRef(TaxonomyData tx) {
return tx.isPortfolio() ? UNITID_PURE : UNITID_CUR;
}
/**
* Adds the generic number unit
*/
void addNumberUnit() {
Element numerUnit = root.addElement("unit");
numerUnit.addAttribute("id", UNITID_PURE);
Element measure = numerUnit.addElement("measure");
measure.addText("xbrli:pure");
}
/**
* Adds the currency unit to the document
*
* @param currencyCode
*/
public void addCurrencyUnit(String currencyCode) {
Element currencyUnitElement = root.addElement("unit");
currencyUnitElement.addAttribute("id", UNITID_CUR);
Element measure = currencyUnitElement.addElement("measure");
measure.addText("iso4217:" + currencyCode);
}
public void addContexts() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
for (Entry<ContextData, String> entry : contextMap.entrySet()) {
ContextData context = entry.getKey();
Element contextElement = root.addElement("context");
contextElement.addAttribute("id", entry.getValue());
contextElement.addElement("entity").addElement("identifier").addAttribute("scheme", SCHEME_URL).addText(IDENTIFIER);
Element periodElement = contextElement.addElement("period");
if (context.getPeriodType() == 0) {
periodElement.addElement("instant").addText(format.format(endDate));
} else {
periodElement.addElement("startDate").addText(format.format(startDate));
periodElement.addElement("endDate").addText(format.format(endDate));
}
String dimension = context.getDimension();
String dimType = context.getDimensionType();
if (dimType != null && dimension != null) {
contextElement.addElement("scenario").addElement("explicitMember").addAttribute("dimension", dimType).addText(dimension);
}
}
}
private Integer getNumberOfDecimalPlaces(BigDecimal bigDecimal) {
String string = bigDecimal.stripTrailingZeros().toPlainString();
int index = string.indexOf(".");
return index < 0 ? 0 : string.length() - index - 1;
}
}
@@ -0,0 +1,11 @@
package org.mifosplatform.xbrl.report.service;
import java.sql.Date;
import org.mifosplatform.xbrl.taxonomy.data.XBRLData;
public interface XBRLResultService {
XBRLData getXBRLResult(Date startDate, Date endDate, String currency);
}
@@ -0,0 +1,192 @@
package org.mifosplatform.xbrl.report.service;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.xbrl.mapping.data.TaxonomyMappingData;
import org.mifosplatform.xbrl.mapping.service.ReadTaxonomyMappingService;
import org.mifosplatform.xbrl.report.exception.XBRLMappingInvalidException;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
import org.mifosplatform.xbrl.taxonomy.data.XBRLData;
import org.mifosplatform.xbrl.taxonomy.service.ReadTaxonomyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Component;
import com.google.gson.Gson;
@Component
public class XBRLResultServiceImpl implements XBRLResultService {
private static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript");
private final ReadTaxonomyMappingService readTaxonomyMappingService;
private final ReadTaxonomyService readTaxonomyService;
private final JdbcTemplate jdbcTemplate;
private HashMap<String, BigDecimal> accountBalanceMap;
@Autowired
public XBRLResultServiceImpl(final RoutingDataSource dataSource, final ReadTaxonomyMappingService readTaxonomyMappingService,
final ReadTaxonomyService readTaxonomyService) {
this.readTaxonomyMappingService = readTaxonomyMappingService;
this.readTaxonomyService = readTaxonomyService;
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public XBRLData getXBRLResult(Date startDate, Date endDate, String currency) {
HashMap<TaxonomyData, BigDecimal> config = this.retrieveTaxonomyConfig(startDate, endDate);
if (config == null || config.size() == 0) { throw new XBRLMappingInvalidException("Mapping is empty"); }
return new XBRLData(config, startDate, endDate, currency);
}
@SuppressWarnings("unchecked")
private HashMap<TaxonomyData, BigDecimal> retrieveTaxonomyConfig(Date startDate, Date endDate) {
TaxonomyMappingData taxonomyMapping = this.readTaxonomyMappingService.retrieveTaxonomyMapping();
if (taxonomyMapping == null) { return null; }
String config = taxonomyMapping.getConfig();
if (config != null) {
// <taxonomyId, mapping>
HashMap<String, String> configMap = new HashMap<String, String>();
configMap = new Gson().fromJson(config, configMap.getClass());
if (configMap == null) { return null; }
// <taxonomyId, value>
HashMap<TaxonomyData, BigDecimal> resultMap = new HashMap<TaxonomyData, BigDecimal>();
this.setupBalanceMap(this.getAccountSql(startDate, endDate));
for (Entry<String, String> entry : configMap.entrySet()) {
BigDecimal value = this.processMappingString(entry.getValue());
if (value != null) {
TaxonomyData taxonomy = readTaxonomyService.retrieveTaxonomyById(Long.parseLong(entry.getKey()));
resultMap.put(taxonomy, value);
}
}
return resultMap;
}
return null;
}
private String getAccountSql(Date startDate, Date endDate) {
String sql = "select debits.glcode as 'glcode', debits.name as 'name', (ifnull(debits.debitamount,0)-ifnull(credits.creditamount,0)) as 'balance' "
+ "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 "
+ "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.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' "
+ "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 glcode "
+ "order by glcode) credits "
+ "on debits.glcode=credits.glcode "
+ "union "
+ "select credits.glcode as 'glcode', credits.name as 'name', (ifnull(debits.debitamount,0)-ifnull(credits.creditamount,0)) as 'balance' "
+ "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 "
+ "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.office_id=${branch} or ${branch}=1) "
// +
" group by glcode "
+ "order by glcode) debits "
+ "RIGHT OUTER JOIN "
+ "(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 " + "order by glcode) credits " + "on debits.glcode=credits.glcode;";
return sql;
}
private void setupBalanceMap(String sql) {
if (accountBalanceMap == null) {
accountBalanceMap = new HashMap<String, BigDecimal>();
SqlRowSet rs = this.jdbcTemplate.queryForRowSet(sql);
while (rs.next()) {
accountBalanceMap.put(rs.getString("glcode"), rs.getBigDecimal("balance"));
}
}
}
// Calculate Taxonomy value from expression
BigDecimal processMappingString(String mappingString) {
ArrayList<String> glCodes = this.getGLCodes(mappingString);
for (String glcode : glCodes) {
BigDecimal balance = accountBalanceMap.get(glcode);
mappingString = mappingString.replaceAll("\\{" + glcode + "\\}", balance != null ? balance.toString() : "0");
}
// evaluate the expression
Float eval = 0f;
try {
Number value = (Number) SCRIPT_ENGINE.eval(mappingString);
if (value != null) {
eval = value.floatValue();
}
} catch (ScriptException e) {
e.printStackTrace();
throw new IllegalArgumentException(e.getMessage());
}
return new BigDecimal(eval);
}
ArrayList<String> getGLCodes(String template) {
ArrayList<String> placeholders = new ArrayList<String>();
if (template != null) {
Pattern p = Pattern.compile("\\{(.*?)\\}");
Matcher m = p.matcher(template);
while (m.find()) { // find next match
String match = m.group();
String code = match.substring(1, match.length() - 1);
placeholders.add(code);
}
}
return placeholders;
}
}
@@ -0,0 +1,58 @@
package org.mifosplatform.xbrl.taxonomy.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.ToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
import org.mifosplatform.xbrl.taxonomy.service.ReadTaxonomyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/mixtaxonomy")
@Component
@Scope("singleton")
public class TaxonomyApiResource {
private final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("taxonomyId", "name", "namespace", "dimension",
"description"));
private final PlatformSecurityContext context;
private final ToApiJsonSerializer<TaxonomyData> toApiJsonSerializer;
private final ReadTaxonomyService readTaxonomyService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@Autowired
public TaxonomyApiResource(final PlatformSecurityContext context, final ToApiJsonSerializer<TaxonomyData> toApiJsonSerializer,
final ReadTaxonomyService readTaxonomyService, final ApiRequestParameterHelper apiRequestParameterHelper) {
this.context = context;
this.toApiJsonSerializer = toApiJsonSerializer;
this.readTaxonomyService = readTaxonomyService;
this.apiRequestParameterHelper = apiRequestParameterHelper;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTaxonomyList(@Context final UriInfo uriInfo) {
context.authenticatedUser();
List<TaxonomyData> taxonomyDatas = this.readTaxonomyService.retrieveAllTaxonomy();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, taxonomyDatas, RESPONSE_DATA_PARAMETERS);
}
}
@@ -0,0 +1,55 @@
package org.mifosplatform.xbrl.taxonomy.data;
public class TaxonomyData {
public static final Integer PORTFOLIO = 0;
public static final Integer BALANCESHEET = 1;
public static final Integer INCOME = 2;
public static final Integer EXPENSE = 3;
private final Long id;
private final String name;
private final String namespace;
private final String dimension;
private final Integer type;
private final String description;
public TaxonomyData(Long id, String name, String namespace, String dimension, Integer type, String description) {
this.id = id;
this.name = name;
this.namespace = namespace;
this.dimension = dimension;
this.type = type;
this.description = description;
}
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getNamespace() {
return this.namespace;
}
public String getDimension() {
return this.dimension;
}
public String getDescription() {
return this.description;
}
public Integer getType() {
return this.type;
}
public boolean isPortfolio() {
return this.type == 5;
}
}
@@ -0,0 +1,37 @@
package org.mifosplatform.xbrl.taxonomy.data;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.HashMap;
public class XBRLData {
private HashMap<TaxonomyData, BigDecimal> resultMap;
private Date startDate;
private Date endDate;
private String currency;
public XBRLData(HashMap<TaxonomyData, BigDecimal> resultMap, Date startDate, Date endDate, String currency) {
this.resultMap = resultMap;
this.startDate = startDate;
this.endDate = endDate;
this.currency = currency;
}
public HashMap<TaxonomyData, BigDecimal> getResultMap() {
return this.resultMap;
}
public Date getStartDate() {
return this.startDate;
}
public Date getEndDate() {
return this.endDate;
}
public String getCurrency() {
return this.currency;
}
}
@@ -0,0 +1,12 @@
package org.mifosplatform.xbrl.taxonomy.service;
import java.util.List;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
public interface ReadTaxonomyService {
List<TaxonomyData> retrieveAllTaxonomy();
TaxonomyData retrieveTaxonomyById(Long id);
}
@@ -0,0 +1,59 @@
package org.mifosplatform.xbrl.taxonomy.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class ReadTaxonomyServiceImpl implements ReadTaxonomyService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public ReadTaxonomyServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
static final class TaxonomyMapper implements RowMapper<TaxonomyData> {
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 TaxonomyData mapRow(ResultSet rs, @SuppressWarnings("unused") 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 TaxonomyData(id, name, namespace, dimension, type, desc);
}
}
@Override
public List<TaxonomyData> retrieveAllTaxonomy() {
final TaxonomyMapper rm = new TaxonomyMapper();
String sql = "select " + rm.schema();
return this.jdbcTemplate.query(sql, rm);
}
@Override
public TaxonomyData retrieveTaxonomyById(Long id) {
final TaxonomyMapper rm = new TaxonomyMapper();
String sql = "select " + rm.schema() + " where tx.id =" + id;
return this.jdbcTemplate.queryForObject(sql, rm);
}
}
@@ -33,7 +33,8 @@
org.mifosplatform.portfolio.loanaccount.*,
org.mifosplatform.portfolio.savingsaccount.*,
org.mifosplatform.portfolio.*,
org.mifosplatform.useradministration.*">
org.mifosplatform.useradministration.*,
org.mifosplatform.xbrl.*">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
@@ -46,6 +47,8 @@
<jpa:repositories base-package="org.mifosplatform.useradministration.domain" />
<jpa:repositories base-package="org.mifosplatform.organisation.*.domain" />
<jpa:repositories base-package="org.mifosplatform.portfolio.*" />
<jpa:repositories base-package="org.mifosplatform.xbrl.*.domain" />
<jpa:repositories base-package="org.mifosplatform.scheduledjobs.domain" />
<import resource="infrastructure.xml" />
@@ -0,0 +1,33 @@
package org.mifosplatform.xbrl.report.service;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ReadTaxonomyMappingServiceImplTest {
XBRLResultServiceImpl readService;
@Before
public void setUp() throws Exception {
RoutingDataSource dataSource = Mockito.mock(RoutingDataSource.class);
readService = new XBRLResultServiceImpl(dataSource, null, null);
}
@Test
public void shouldCorrectlyGetGLCode() {
ArrayList<String> result = readService.getGLCodes("{12000}+{11000}");
assertEquals("12000", result.get(0));
assertEquals("11000", result.get(1));
}
}
@@ -0,0 +1,73 @@
package org.mifosplatform.xbrl.report.service;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mifosplatform.xbrl.report.data.NamespaceData;
import org.mifosplatform.xbrl.taxonomy.data.TaxonomyData;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
@RunWith(MockitoJUnitRunner.class)
public class XBRLBuilderTest {
@Mock
private ReadNamespaceServiceImpl readNamespaceService;
@InjectMocks
private XBRLBuilder xbrlBuilder = new XBRLBuilder();
@Before
public void setUp() throws Exception {
readNamespaceService = Mockito.mock(ReadNamespaceServiceImpl.class);
when(readNamespaceService.retrieveNamespaceByPrefix(Mockito.anyString())).thenReturn(
new NamespaceData(1l, "mockedprefix", "mockedurl"));
}
@Test
public void shouldCorrectlyBuildMap() {
HashMap<TaxonomyData, BigDecimal> map = new HashMap<TaxonomyData, BigDecimal>();
TaxonomyData data1 = Mockito.mock(TaxonomyData.class);
when(data1.getName()).thenReturn("Assets");
map.put(data1, new BigDecimal(10000));
String result = xbrlBuilder.build(map, Date.valueOf("2005-11-11"), Date.valueOf("2013-07-17"), "USD");
System.out.println(result);
NodeList nodes = null;
try {
nodes = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(result.getBytes()))
.getElementsByTagName("Assets");
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
assertNotNull(nodes);
assertNotNull(nodes.item(0));
assertEquals("Assets", nodes.item(0).getNodeName());
}
}