Rename root folders

This commit is contained in:
Adi Narayana Raju
2016-02-01 11:00:15 +05:30
parent c5b56dae8f
commit 139e066d3d
2652 changed files with 0 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
Welcome to the Mifos community!
===============================
Depending on the OS you're running, you should have "runmifosx.sh" (for Linux and Mac) and
"runmifosx.bat" (for Windows) in this directory. These are small scripts that allow you to
run MifosX on your computer locally. To be able to run MifosX, just double-click on the script,
that is, "runmifosx.sh" if you are on Linux or Mac, or "runmifosx.bat" if you are on Windows.
If you have a web front-end that you want to use along with the backend server, put your index.html
along with all the other source files inside a (sub-folder of) the "apps" folder in the current
directory from where runmifosx.bat/.sh is launched. The web UI front-end is then accessible
under mifosng-provider/apps - for example:
https://localhost:8443/mifosng-provider/apps/community-app/index.html?baseApiUrl=https://localhost:8443&tenantIdentifier=default#/home
We hope you'll have an amazing experience working with MifosX!
PS, internal note to Mifos developers preparing packages, only: For this to work, the WAR of
the Mifos X distribution ZIP package will (currently) have to be built using
the Gradle 'dev' environment profile - e.g. using this command:
./gradlew -Penv=dev clean war dist
We now also have a script which convenently prepares both the
UI front-end and the API back-end together into a ZIP:
git clone --recursive https://github.com/openMF/mifosx/
git submodule update --init --recursive; git submodule status
./build.sh
+10
View File
@@ -0,0 +1,10 @@
java -version >nul 2>&1 && (
echo "Found Java."
) || (
echo "Java should be installed to run MifosX."
exit
)
echo "Starting MifosX ..... "
java -Djava.awt.headless=false -jar mifosng-provider.war
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
# First of all, check if Java is installed on this computer
if hash java; then
echo "Found Java:"
echo $(java -version)
else
echo "Java should be installed to run MifosX."
exit
fi
echo "Starting MifosX ..... "
java -Djava.awt.headless=false -jar mifosng-provider.war
@@ -0,0 +1,43 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform;
import org.mifosplatform.infrastructure.core.boot.AbstractApplicationConfiguration;
import org.mifosplatform.infrastructure.core.boot.ApplicationExitUtil;
import org.mifosplatform.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration;
import org.mifosplatform.infrastructure.core.boot.db.DataSourceConfiguration;
import org.mifosplatform.infrastructure.core.boot.db.DataSourceProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
/**
* Mifos main() application which launches Mifos X in an embedded Tomcat HTTP
* (using Spring Boot).
*
* The DataSource used is a to a "normal" external database (not use MariaDB4j).
* This DataSource can be configured with parameters, see {@link DataSourceProperties}.
*
* You can easily launch this via Debug as Java Application in your IDE -
* without needing command line Gradle stuff, no need to build and deploy a WAR,
* remote attachment etc.
*
* It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Mifos X! ;-)
*
* @see DataSourceProperties about how to configure the DataSource used
* @see ServerWithMariaDB4jApplication for an alternative with an embedded DB
*/
public class ServerApplication {
@Import({ DataSourceConfiguration.class, EmbeddedTomcatWithSSLConfiguration.class })
private static class Configuration extends AbstractApplicationConfiguration { }
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = SpringApplication.run(Configuration.class, args);
ApplicationExitUtil.waitForKeyPressToCleanlyExit(ctx);
}
}
@@ -0,0 +1,92 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform;
import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import org.mifosplatform.infrastructure.core.boot.AbstractApplicationConfiguration;
import org.mifosplatform.infrastructure.core.boot.ApplicationExitUtil;
import org.mifosplatform.infrastructure.core.boot.EmbeddedTomcatWithSSLConfiguration;
import org.mifosplatform.infrastructure.core.boot.db.MariaDB4jDataSourceConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
/**
* Mifos main() application which launches Mifos X in an embedded Tomcat HTTP
* server (using Spring Boot), as well as an embedded database (using
* MariaDB4j).
*
* You can easily launch this via Debug as Java Application in your IDE -
* without needing command line Gradle stuff, no need to build and deploy a WAR,
* remote attachment etc.
*
* It's the old/classic Mifos (non-X) Workspace 2.0 reborn for Mifos X! ;-)
*
* @see ServerApplication for the same without the embedded MariaDB4j database
*/
public class ServerWithMariaDB4jApplication {
private final static Logger logger = LoggerFactory.getLogger(ServerWithMariaDB4jApplication.class);
@Import({ MariaDB4jDataSourceConfiguration.class, EmbeddedTomcatWithSSLConfiguration.class })
public static class Configuration extends AbstractApplicationConfiguration { }
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext ctx = SpringApplication.run(Configuration.class, args);
if (!Desktop.isDesktopSupported()) {
logger.info("Not going to open UI homepage in local web browser, because !Desktop.isDesktopSupported()");
} else {
// apps/community-app/dist/community-app/index.html
Resource distResource = ctx.getResource("file:" + System.getProperty("user.dir") +
System.getProperty("file.separator") + "apps" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "dist" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "index.html");
URI distURI = URI.create("https://localhost:8443/mifosng-provider" +
"/apps/community-app/index.html?baseApiUrl=https://localhost:8443" +
"&tenantIdentifier=default#/");
// apps/community-app/app/index.html
Resource devResource = ctx.getResource("file:" + System.getProperty("user.dir") +
System.getProperty("file.separator") + "apps" +
System.getProperty("file.separator") + "community-app" +
System.getProperty("file.separator") + "app" +
System.getProperty("file.separator") + "index.html");
URI devURI = URI.create("https://localhost:8443/mifosng-provider" +
"/apps/community-app/app/index.html?baseApiUrl=https://localhost:8443" +
"&tenantIdentifier=default#/");
if (distResource.exists()) {
openWebBrowser(distURI);
} else if (devResource.exists()) {
openWebBrowser(devURI);
} else {
logger.error("Cannot open Mifos X UI in browser; not found: " + distResource.toString());
}
}
// TODO Tray Icon stuff; dig out my very own old @see https://github.com/mifos/head/tree/hudsonBuild-MIFOS-5157_Launch4j-EXE_NewDist-squash1/server-jetty/src/main/java/org/mifos/server/tray
ApplicationExitUtil.waitForKeyPressToCleanlyExit(ctx);
}
private static void openWebBrowser(URI uri) {
try {
logger.info("Opening Mifos X UI in browser: " + uri.toString());
Desktop.getDesktop().browse(uri);
} catch (IOException e) {
logger.error("IOException when opening Mifos X UI in browser: " + uri.toString(), e);
}
}
}
@@ -0,0 +1,51 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.api;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/runaccruals")
@Component
@Scope("singleton")
public class AccrualAccountingApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<String> apiJsonSerializerService;
@Autowired
public AccrualAccountingApiResource(final DefaultToApiJsonSerializer<String> apiJsonSerializerService,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = apiJsonSerializerService;
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String executePeriodicAccrualAccounting(final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().excuteAccrualAccounting().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
}
@@ -0,0 +1,23 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AccrualAccountingConstants {
public static final String accrueTillParamName = "tillDate";
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME = "periodicaccrual";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE = "execution.failed";
public static final Set<String> LOAN_PERIODIC_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(accrueTillParamName,
localeParamName, dateFormatParamName));
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.handler;
import org.mifosplatform.accounting.accrual.service.AccrualAccountingWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PERIODICACCRUALACCOUNTING", action = "EXECUTE")
public class ExecutePeriodicAccrualCommandHandler implements NewCommandSourceHandler {
private final AccrualAccountingWritePlatformService writePlatformService;
@Autowired
public ExecutePeriodicAccrualCommandHandler(final AccrualAccountingWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.executeLoansPeriodicAccrual(command);
}
}
@@ -0,0 +1,67 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.serialization;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.LOAN_PERIODIC_REQUEST_DATA_PARAMETERS;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.infrastructure.core.service.DateUtils;
import org.mifosplatform.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for
* {@link GuarantorCommand}'s.
*/
@Component
public final class AccrualAccountingDataValidator {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public AccrualAccountingDataValidator(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
}
public void validateLoanPeriodicAccrualData(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, LOAN_PERIODIC_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
final LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(accrueTillParamName, element);
baseDataValidator.reset().parameter(accrueTillParamName).value(date).notNull().validateDateBefore(DateUtils.getLocalDateOfTenant());
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
}
}
@@ -0,0 +1,15 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface AccrualAccountingWritePlatformService {
CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command);
}
@@ -0,0 +1,54 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.accrual.service;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
import static org.mifosplatform.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.accrual.serialization.AccrualAccountingDataValidator;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.portfolio.loanaccount.service.LoanAccrualPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccrualAccountingWritePlatformServiceImpl implements AccrualAccountingWritePlatformService {
private final LoanAccrualPlatformService loanAccrualPlatformService;
private final AccrualAccountingDataValidator accountingDataValidator;
@Autowired
public AccrualAccountingWritePlatformServiceImpl(final LoanAccrualPlatformService loanAccrualPlatformService,
final AccrualAccountingDataValidator accountingDataValidator) {
this.loanAccrualPlatformService = loanAccrualPlatformService;
this.accountingDataValidator = accountingDataValidator;
}
@Override
public CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command) {
this.accountingDataValidator.validateLoanPeriodicAccrualData(command.json());
LocalDate tilldate = command.localDateValueOfParameterNamed(accrueTillParamName);
String errorlog = this.loanAccrualPlatformService.addPeriodicAccruals(tilldate);
if (errorlog.length() > 0) {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE, errorlog);
throw new PlatformApiDataValidationException(dataValidationErrors);
}
return CommandProcessingResult.empty();
}
}
@@ -0,0 +1,42 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a loan product
***/
public enum GLClosureJsonInputParams {
ID("id"), OFFICE_ID("officeId"), CLOSING_DATE("closingDate"), COMMENTS("comments"), LOCALE("locale"), DATE_FORMAT("dateFormat");
private final String value;
private GLClosureJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final GLClosureJsonInputParams type : GLClosureJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,138 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.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.DELETE;
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.accounting.closure.data.GLClosureData;
import org.mifosplatform.accounting.closure.service.GLClosureReadPlatformService;
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.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.service.OfficeReadPlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/glclosures")
@Component
@Scope("singleton")
public class GLClosuresApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "officeId", "officeName",
"closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId", "createdByUsername", "lastUpdatedByUserId",
"lastUpdatedByUsername"));
private final String resourceNameForPermission = "GLCLOSURE";
private final GLClosureReadPlatformService glClosureReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService;
private final DefaultToApiJsonSerializer<GLClosureData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public GLClosuresApiResource(final PlatformSecurityContext context, final GLClosureReadPlatformService glClosureReadPlatformService,
final DefaultToApiJsonSerializer<GLClosureData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final OfficeReadPlatformService officeReadPlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.glClosureReadPlatformService = glClosureReadPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllClosures(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final List<GLClosureData> glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glClosureDatas, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveClosure(@PathParam("glClosureId") final Long glClosureId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
if (settings.isTemplate()) {
glClosureData.setAllowedOffices(this.officeReadPlatformService.retrieveAllOfficesForDropdown());
}
return this.apiJsonSerializerService.serialize(settings, glClosureData, RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLClosure(final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLClosure().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@PUT
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateGLClosure(@PathParam("glClosureId") final Long glClosureId, final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLClosure(glClosureId).withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@DELETE
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteGLClosure(@PathParam("glClosureId") final Long glClosureId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLClosure(glClosureId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
}
@@ -0,0 +1,64 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.command;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
/**
* Immutable command for adding an accounting closure
*/
public class GLClosureCommand {
@SuppressWarnings("unused")
private final Long id;
private final Long officeId;
private final LocalDate closingDate;
private final String comments;
public GLClosureCommand(final Long id, final Long officeId, final LocalDate closingDate, final String comments) {
this.id = id;
this.officeId = officeId;
this.closingDate = closingDate;
this.comments = comments;
}
public void validateForCreate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter(GLClosureJsonInputParams.CLOSING_DATE.getValue()).value(this.closingDate).notBlank();
baseDataValidator.reset().parameter(GLClosureJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).notNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull()
.notExceedingLengthOf(500);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull()
.notExceedingLengthOf(500);
baseDataValidator.reset().anyOfNotNull(this.comments);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -0,0 +1,75 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.data;
import java.util.ArrayList;
import java.util.Collection;
import org.joda.time.LocalDate;
import org.mifosplatform.organisation.office.data.OfficeData;
/**
* Immutable object representing a General Ledger Account
*
* Note: no getter/setters required as google-gson will produce json from fields
* of object.
*/
public class GLClosureData {
@SuppressWarnings("unused")
private final Long id;
@SuppressWarnings("unused")
private final Long officeId;
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final LocalDate closingDate;
@SuppressWarnings("unused")
private final boolean deleted;
@SuppressWarnings("unused")
private final LocalDate createdDate;
@SuppressWarnings("unused")
private final LocalDate lastUpdatedDate;
@SuppressWarnings("unused")
private final Long createdByUserId;
@SuppressWarnings("unused")
private final String createdByUsername;
@SuppressWarnings("unused")
private final Long lastUpdatedByUserId;
@SuppressWarnings("unused")
private final String lastUpdatedByUsername;
@SuppressWarnings("unused")
private final String comments;
private Collection<OfficeData> allowedOffices = new ArrayList<>();
public GLClosureData(final Long id, final Long officeId, final String officeName, final LocalDate closingDate, final boolean deleted,
final LocalDate createdDate, final LocalDate lastUpdatedDate, final Long createdByUserId, final String createdByUsername,
final Long lastUpdatedByUserId, final String lastUpdatedByUsername, final String comments) {
this.id = id;
this.officeId = officeId;
this.officeName = officeName;
this.closingDate = closingDate;
this.deleted = deleted;
this.createdDate = createdDate;
this.lastUpdatedDate = lastUpdatedDate;
this.createdByUserId = createdByUserId;
this.createdByUsername = createdByUsername;
this.lastUpdatedByUserId = lastUpdatedByUserId;
this.lastUpdatedByUsername = lastUpdatedByUsername;
this.comments = comments;
this.allowedOffices = null;
}
public final Collection<OfficeData> getAllowedOffices() {
return this.allowedOffices;
}
public void setAllowedOffices(final Collection<OfficeData> allowedOffices) {
this.allowedOffices = allowedOffices;
}
}
@@ -0,0 +1,92 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.domain;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@Table(name = "acc_gl_closure", uniqueConstraints = { @UniqueConstraint(columnNames = { "office_id", "closing_date" }, name = "office_id_closing_date") })
public class GLClosure extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@Column(name = "is_deleted", nullable = false)
private boolean deleted = true;
@Column(name = "closing_date")
@Temporal(TemporalType.DATE)
private Date closingDate;
@Column(name = "comments", nullable = true, length = 500)
private String comments;
protected GLClosure() {
//
}
public GLClosure(final Office office, final Date closingDate, final String comments) {
this.office = office;
this.deleted = false;
this.closingDate = closingDate;
this.comments = StringUtils.defaultIfEmpty(comments, null);
if (this.comments != null) {
this.comments = this.comments.trim();
}
}
public static GLClosure fromJson(final Office office, final JsonCommand command) {
final Date closingDate = command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
final String comments = command.stringValueOfParameterNamed(GLClosureJsonInputParams.COMMENTS.getValue());
return new GLClosure(office, closingDate, comments);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
handlePropertyUpdate(command, actualChanges, GLClosureJsonInputParams.COMMENTS.getValue(), this.comments);
return actualChanges;
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(GLClosureJsonInputParams.COMMENTS.getValue())) {
this.comments = newValue;
}
}
}
public Date getClosingDate() {
return this.closingDate;
}
public Office getOffice() {
return this.office;
}
}
@@ -0,0 +1,17 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface GLClosureRepository extends JpaRepository<GLClosure, Long>, JpaSpecificationExecutor<GLClosure> {
@Query("from GLClosure closure where closure.closingDate = (select max(closure1.closingDate) from GLClosure closure1 where closure1.office.id=:officeId) and closure.office.id= :officeId")
GLClosure getLatestGLClosureByBranch(@Param("officeId") Long officeId);
}
@@ -0,0 +1,22 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.exception;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Closure for a given date and
* Office combination is already present
*/
public class GLClosureDuplicateException extends AbstractPlatformDomainRuleException {
public GLClosureDuplicateException(final Long officeId, final LocalDate closureDate) {
super("error.msg.glclosure.glcode.duplicate", "An accounting closure for branch with Id " + officeId
+ " already exists for the date " + closureDate, officeId, closureDate);
}
}
@@ -0,0 +1,21 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.exception;
import java.util.Date;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Closure Delte command is invalid
*/
public class GLClosureInvalidDeleteException extends AbstractPlatformDomainRuleException {
public GLClosureInvalidDeleteException(final Long officeId, final String officeName, final Date latestclosureDate) {
super("error.msg.glclosure.invalid.delete", "The latest closure for office with Id " + officeId + " and name " + officeName
+ " is on " + latestclosureDate.toString() + ", please delete this closure first", latestclosureDate);
}
}
@@ -0,0 +1,39 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.exception;
import java.util.Date;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Closure is Invalid
*/
public class GLClosureInvalidException extends AbstractPlatformDomainRuleException {
/*** enum of reasons for invalid Accounting Closure **/
public static enum GL_CLOSURE_INVALID_REASON {
FUTURE_DATE, ACCOUNTING_CLOSED;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "Accounting closures cannot be made for a future date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "Accounting Closure for this branch has already been defined for a greater date"; }
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "error.msg.glclosure.invalid.future.date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "error.msg.glclosure.invalid.accounting.closed"; }
return name().toString();
}
}
public GLClosureInvalidException(final GL_CLOSURE_INVALID_REASON reason, final Date date) {
super(reason.errorCode(), reason.errorMessage(), date);
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when GL account closure resources are not
* found.
*/
public class GLClosureNotFoundException extends AbstractPlatformResourceNotFoundException {
public GLClosureNotFoundException(final Long id) {
super("error.msg.glclosure.id.invalid", "Accounting Closure with identifier " + id + " does not exist", id);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLCLOSURE", action = "CREATE")
public class CreateGLClosureCommandHandler implements NewCommandSourceHandler {
private final GLClosureWritePlatformService writePlatformService;
@Autowired
public CreateGLClosureCommandHandler(final GLClosureWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createGLClosure(command);
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLCLOSURE", action = "DELETE")
public class DeleteGLClosureCommandHandler implements NewCommandSourceHandler {
private final GLClosureWritePlatformService closureWritePlatformService;
@Autowired
public DeleteGLClosureCommandHandler(final GLClosureWritePlatformService guarantorWritePlatformService) {
this.closureWritePlatformService = guarantorWritePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.closureWritePlatformService.deleteGLClosure(command.entityId());
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLCLOSURE", action = "UPDATE")
public class UpdateGLClosureCommandHandler implements NewCommandSourceHandler {
private final GLClosureWritePlatformService writePlatformService;
@Autowired
public UpdateGLClosureCommandHandler(final GLClosureWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.updateGLClosure(command.entityId(), command);
}
}
@@ -0,0 +1,59 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.serialization;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
import org.mifosplatform.accounting.closure.command.GLClosureCommand;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for
* {@link GuarantorCommand}'s.
*/
@Component
public final class GLClosureCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<GLClosureCommand> {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public GLClosureCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
}
@Override
public GLClosureCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = GLClosureJsonInputParams.getAllValues();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long id = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.ID.getValue(), element);
final Long officeId = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.OFFICE_ID.getValue(), element);
final String comments = this.fromApiJsonHelper.extractStringNamed(GLClosureJsonInputParams.COMMENTS.getValue(), element);
final LocalDate closingDate = this.fromApiJsonHelper.extractLocalDateNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue(),
element);
return new GLClosureCommand(id, officeId, closingDate, comments);
}
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.service;
import java.util.List;
import org.mifosplatform.accounting.closure.data.GLClosureData;
public interface GLClosureReadPlatformService {
List<GLClosureData> retrieveAllGLClosures(Long OfficeId);
GLClosureData retrieveGLClosureById(long glClosureId);
}
@@ -0,0 +1,101 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.closure.data.GLClosureData;
import org.mifosplatform.accounting.closure.exception.GLClosureNotFoundException;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
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 GLClosureReadPlatformServiceImpl implements GLClosureReadPlatformService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public GLClosureReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class GLClosureMapper implements RowMapper<GLClosureData> {
public String schema() {
return " glClosure.id as id, glClosure.office_id as officeId,office.name as officeName ,glClosure.closing_date as closingDate,"
+ " glClosure.is_deleted as isDeleted, creatingUser.id as creatingUserId,creatingUser.username as creatingUserName,"
+ " updatingUser.id as updatingUserId,updatingUser.username as updatingUserName, glClosure.created_date as createdDate,"
+ " glClosure.lastmodified_date as updatedDate, glClosure.comments as comments "
+ " from acc_gl_closure as glClosure, m_appuser as creatingUser, m_appuser as updatingUser,m_office as office"
+ " where glClosure.createdby_id=creatingUser.id and "
+ " glClosure.lastmodifiedby_id=updatingUser.id and glClosure.office_id=office.id";
}
@Override
public GLClosureData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long officeId = rs.getLong("officeId");
final String officeName = rs.getString("officeName");
final LocalDate closingDate = JdbcSupport.getLocalDate(rs, "closingDate");
final Boolean deleted = rs.getBoolean("isDeleted");
final LocalDate createdDate = JdbcSupport.getLocalDate(rs, "createdDate");
final LocalDate lastUpdatedDate = JdbcSupport.getLocalDate(rs, "updatedDate");
final Long creatingByUserId = rs.getLong("creatingUserId");
final String createdByUserName = rs.getString("creatingUserName");
final Long lastUpdatedByUserId = rs.getLong("updatingUserId");
final String lastUpdatedByUserName = rs.getString("updatingUserName");
final String comments = rs.getString("comments");
return new GLClosureData(id, officeId, officeName, closingDate, deleted, createdDate, lastUpdatedDate, creatingByUserId,
createdByUserName, lastUpdatedByUserId, lastUpdatedByUserName, comments);
}
}
@Override
public List<GLClosureData> retrieveAllGLClosures(final Long officeId) {
final GLClosureMapper rm = new GLClosureMapper();
String sql = "select " + rm.schema() + " and glClosure.is_deleted = 0";
final Object[] objectArray = new Object[1];
int arrayPos = 0;
if (officeId != null && officeId != 0) {
sql += " and glClosure.office_id = ?";
objectArray[arrayPos] = officeId;
arrayPos = arrayPos + 1;
}
sql = sql + " order by glClosure.closing_date desc";
final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray);
}
@Override
public GLClosureData retrieveGLClosureById(final long glClosureId) {
try {
final GLClosureMapper rm = new GLClosureMapper();
final String sql = "select " + rm.schema() + " and glClosure.id = ?";
final GLClosureData glAccountData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { glClosureId });
return glAccountData;
} catch (final EmptyResultDataAccessException e) {
throw new GLClosureNotFoundException(glClosureId);
}
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface GLClosureWritePlatformService {
CommandProcessingResult createGLClosure(JsonCommand command);
CommandProcessingResult updateGLClosure(Long glClosureId, JsonCommand command);
CommandProcessingResult deleteGLClosure(Long glClosureId);
}
@@ -0,0 +1,143 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.closure.service;
import java.util.Date;
import java.util.Map;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams;
import org.mifosplatform.accounting.closure.command.GLClosureCommand;
import org.mifosplatform.accounting.closure.domain.GLClosure;
import org.mifosplatform.accounting.closure.domain.GLClosureRepository;
import org.mifosplatform.accounting.closure.exception.GLClosureDuplicateException;
import org.mifosplatform.accounting.closure.exception.GLClosureInvalidDeleteException;
import org.mifosplatform.accounting.closure.exception.GLClosureInvalidException;
import org.mifosplatform.accounting.closure.exception.GLClosureInvalidException.GL_CLOSURE_INVALID_REASON;
import org.mifosplatform.accounting.closure.exception.GLClosureNotFoundException;
import org.mifosplatform.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer;
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.core.exception.PlatformDataIntegrityException;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.organisation.office.domain.OfficeRepository;
import org.mifosplatform.organisation.office.exception.OfficeNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosureWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GLClosureWritePlatformServiceJpaRepositoryImpl.class);
private final GLClosureRepository glClosureRepository;
private final OfficeRepository officeRepository;
private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Autowired
public GLClosureWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final OfficeRepository officeRepository, final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@Transactional
@Override
public CommandProcessingResult createGLClosure(final JsonCommand command) {
try {
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForCreate();
// check office is valid
final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
final Office office = this.officeRepository.findOne(officeId);
if (office == null) { throw new OfficeNotFoundException(officeId); }
// TODO: Get Tenant specific date
// ensure closure date is not in the future
final Date todaysDate = new Date();
final Date closureDate = command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(GL_CLOSURE_INVALID_REASON.FUTURE_DATE, closureDate); }
// shouldn't be before an existing accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(officeId);
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
}
final GLClosure glClosure = GLClosure.fromJson(office, command);
this.glClosureRepository.saveAndFlush(glClosure);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withEntityId(glClosure.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleGLClosureIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult updateGLClosure(final Long glClosureId, final JsonCommand command) {
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForUpdate();
// is the glClosure valid
final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
final Map<String, Object> changesOnly = glClosure.update(command);
if (!changesOnly.isEmpty()) {
this.glClosureRepository.saveAndFlush(glClosure);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(glClosure.getOffice().getId())
.withEntityId(glClosure.getId()).with(changesOnly).build();
}
@Transactional
@Override
public CommandProcessingResult deleteGLClosure(final Long glClosureId) {
final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
/**
* check if any closures are present for this branch at a later date
* than this closure date
**/
final Date closureDate = glClosure.getClosingDate();
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(glClosure.getOffice().getId());
if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidDeleteException(latestGLClosure.getOffice()
.getId(), latestGLClosure.getOffice().getName(), latestGLClosure.getClosingDate()); }
this.glClosureRepository.delete(glClosure);
return new CommandProcessingResultBuilder().withOfficeId(glClosure.getOffice().getId()).withEntityId(glClosure.getId()).build();
}
/**
* @param command
* @param dve
*/
private void handleGLClosureIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("office_id_closing_date")) { throw new GLClosureDuplicateException(
command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue()), new LocalDate(
command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue()))); }
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
"Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
}
}
@@ -0,0 +1,301 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.common;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.financialactivityaccount.data.FinancialActivityData;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
public class AccountingConstants {
/*** Accounting placeholders for cash based accounting for loan products ***/
public static enum CASH_ACCOUNTS_FOR_LOAN {
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), TRANSFERS_SUSPENSE(
10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
private final Integer value;
private CASH_ACCOUNTS_FOR_LOAN(final Integer value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
private static final Map<Integer, CASH_ACCOUNTS_FOR_LOAN> intToEnumMap = new HashMap<>();
static {
for (final CASH_ACCOUNTS_FOR_LOAN type : CASH_ACCOUNTS_FOR_LOAN.values()) {
intToEnumMap.put(type.value, type);
}
}
public static CASH_ACCOUNTS_FOR_LOAN fromInt(final int i) {
final CASH_ACCOUNTS_FOR_LOAN type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
}
/*** Accounting placeholders for accrual based accounting for loan products ***/
public static enum ACCRUAL_ACCOUNTS_FOR_LOAN {
FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(
7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
private final Integer value;
private ACCRUAL_ACCOUNTS_FOR_LOAN(final Integer value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
private static final Map<Integer, ACCRUAL_ACCOUNTS_FOR_LOAN> intToEnumMap = new HashMap<>();
static {
for (final ACCRUAL_ACCOUNTS_FOR_LOAN type : ACCRUAL_ACCOUNTS_FOR_LOAN.values()) {
intToEnumMap.put(type.value, type);
}
}
public static ACCRUAL_ACCOUNTS_FOR_LOAN fromInt(final int i) {
final ACCRUAL_ACCOUNTS_FOR_LOAN type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
}
/***
* Enum of all accounting related input parameter names used while
* creating/updating a loan product
***/
public static enum LOAN_PRODUCT_ACCOUNTING_PARAMS {
FUND_SOURCE("fundSourceAccountId"), LOAN_PORTFOLIO("loanPortfolioAccountId"), INTEREST_ON_LOANS("interestOnLoanAccountId"), INCOME_FROM_FEES(
"incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), LOSSES_WRITTEN_OFF("writeOffAccountId"), OVERPAYMENT(
"overpaymentLiabilityAccountId"), INTEREST_RECEIVABLE("receivableInterestAccountId"), FEES_RECEIVABLE(
"receivableFeeAccountId"), PENALTIES_RECEIVABLE("receivablePenaltyAccountId"), TRANSFERS_SUSPENSE(
"transfersInSuspenseAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
"paymentTypeId"), FEE_INCOME_ACCOUNT_MAPPING("feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING(
"penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID("incomeAccountId"), INCOME_FROM_RECOVERY(
"incomeFromRecoveryAccountId");
private final String value;
private LOAN_PRODUCT_ACCOUNTING_PARAMS(final String value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static enum LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS {
FUND_SOURCE("fundSourceAccount"), LOAN_PORTFOLIO("loanPortfolioAccount"), INTEREST_ON_LOANS("interestOnLoanAccount"), INCOME_FROM_FEES(
"incomeFromFeeAccount"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccount"), LOSSES_WRITTEN_OFF("writeOffAccount"), OVERPAYMENT(
"overpaymentLiabilityAccount"), INTEREST_RECEIVABLE("receivableInterestAccount"), FEES_RECEIVABLE("receivableFeeAccount"), PENALTIES_RECEIVABLE(
"receivablePenaltyAccount"), TRANSFERS_SUSPENSE("transfersInSuspenseAccount"), INCOME_ACCOUNT_ID("incomeAccount"), INCOME_FROM_RECOVERY(
"incomeFromRecoveryAccount"), LIABILITY_TRANSFER_SUSPENSE("liabilityTransferInSuspenseAccount");
private final String value;
private LOAN_PRODUCT_ACCOUNTING_DATA_PARAMS(final String value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
/*** Accounting placeholders for cash based accounting for savings products ***/
public static enum CASH_ACCOUNTS_FOR_SAVINGS {
SAVINGS_REFERENCE(1), SAVINGS_CONTROL(2), INTEREST_ON_SAVINGS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), TRANSFERS_SUSPENSE(
10), OVERDRAFT_PORTFOLIO_CONTROL(11), INCOME_FROM_INTEREST(12), LOSSES_WRITTEN_OFF(13);
private final Integer value;
private CASH_ACCOUNTS_FOR_SAVINGS(final Integer value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
private static final Map<Integer, CASH_ACCOUNTS_FOR_SAVINGS> intToEnumMap = new HashMap<>();
static {
for (final CASH_ACCOUNTS_FOR_SAVINGS type : CASH_ACCOUNTS_FOR_SAVINGS.values()) {
intToEnumMap.put(type.value, type);
}
}
public static CASH_ACCOUNTS_FOR_SAVINGS fromInt(final int i) {
final CASH_ACCOUNTS_FOR_SAVINGS type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
}
/***
* Enum of all accounting related input parameter names used while
* creating/updating a savings product
***/
public static enum SAVINGS_PRODUCT_ACCOUNTING_PARAMS {
SAVINGS_REFERENCE("savingsReferenceAccountId"), SAVINGS_CONTROL("savingsControlAccountId"), INCOME_FROM_FEES(
"incomeFromFeeAccountId"), INCOME_FROM_PENALTIES("incomeFromPenaltyAccountId"), INTEREST_ON_SAVINGS(
"interestOnSavingsAccountId"), PAYMENT_CHANNEL_FUND_SOURCE_MAPPING("paymentChannelToFundSourceMappings"), PAYMENT_TYPE(
"paymentTypeId"), FUND_SOURCE("fundSourceAccountId"), TRANSFERS_SUSPENSE("transfersInSuspenseAccountId"), FEE_INCOME_ACCOUNT_MAPPING(
"feeToIncomeAccountMappings"), PENALTY_INCOME_ACCOUNT_MAPPING("penaltyToIncomeAccountMappings"), CHARGE_ID("chargeId"), INCOME_ACCOUNT_ID(
"incomeAccountId"), OVERDRAFT_PORTFOLIO_CONTROL("overdraftPortfolioControlId"), INCOME_FROM_INTEREST("incomeFromInterestId"), LOSSES_WRITTEN_OFF(
"writeOffAccountId");
private final String value;
private SAVINGS_PRODUCT_ACCOUNTING_PARAMS(final String value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static enum SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS {
SAVINGS_REFERENCE("savingsReferenceAccount"), SAVINGS_CONTROL("savingsControlAccount"), INCOME_FROM_FEES("incomeFromFeeAccount"), INCOME_FROM_PENALTIES(
"incomeFromPenaltyAccount"), INTEREST_ON_SAVINGS("interestOnSavingsAccount"), PAYMENT_TYPE("paymentType"), FUND_SOURCE(
"fundSourceAccount"), TRANSFERS_SUSPENSE("transfersInSuspenseAccount"), PENALTY_INCOME_ACCOUNT_MAPPING(
"penaltyToIncomeAccountMappings"), CHARGE_ID("charge"), INCOME_ACCOUNT_ID("incomeAccount"), OVERDRAFT_PORTFOLIO_CONTROL(
"overdraftPortfolioControl"), INCOME_FROM_INTEREST("incomeFromInterest"), LOSSES_WRITTEN_OFF("writeOffAccount");
private final String value;
private SAVINGS_PRODUCT_ACCOUNTING_DATA_PARAMS(final String value) {
this.value = value;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
public static enum FINANCIAL_ACTIVITY {
ASSET_TRANSFER(100, "assetTransfer", GLAccountType.ASSET), LIABILITY_TRANSFER(200, "liabilityTransfer", GLAccountType.LIABILITY),
CASH_AT_MAINVAULT (101, "cashAtMainVault", GLAccountType.ASSET),
CASH_AT_TELLER (102, "cashAtTeller", GLAccountType.ASSET),OPENING_BALANCES_TRANSFER_CONTRA (300,"openingBalancesTransferContra",GLAccountType.EQUITY),
ASSET_FUND_SOURCE(103, "fundSource", GLAccountType.ASSET);
private final Integer value;
private final String code;
private final GLAccountType mappedGLAccountType;
private static List<FinancialActivityData> financialActivities;
private FINANCIAL_ACTIVITY(final Integer value, final String code, final GLAccountType mappedGLAccountType) {
this.value = value;
this.code = code;
this.mappedGLAccountType = mappedGLAccountType;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
public GLAccountType getMappedGLAccountType() {
return mappedGLAccountType;
}
public String getValueAsString() {
return this.value.toString();
}
private static final Map<Integer, FINANCIAL_ACTIVITY> intToEnumMap = new HashMap<>();
static {
for (final FINANCIAL_ACTIVITY type : FINANCIAL_ACTIVITY.values()) {
intToEnumMap.put(type.value, type);
}
}
public static FINANCIAL_ACTIVITY fromInt(final int financialActivityId) {
final FINANCIAL_ACTIVITY type = intToEnumMap.get(Integer.valueOf(financialActivityId));
return type;
}
public static FinancialActivityData toFinancialActivityData(final int financialActivityId) {
final FINANCIAL_ACTIVITY type = fromInt(financialActivityId);
return convertToFinancialActivityData(type);
}
public static List<FinancialActivityData> getAllFinancialActivities() {
if (financialActivities == null) {
financialActivities = new ArrayList<>();
for (final FINANCIAL_ACTIVITY type : FINANCIAL_ACTIVITY.values()) {
FinancialActivityData financialActivityData = convertToFinancialActivityData(type);
financialActivities.add(financialActivityData);
}
}
return financialActivities;
}
private static FinancialActivityData convertToFinancialActivityData(final FINANCIAL_ACTIVITY type) {
FinancialActivityData financialActivityData = new FinancialActivityData(type.value, type.code, type.getMappedGLAccountType());
return financialActivityData;
}
}
public static final String ASSESTS_TAG_OPTION_CODE_NAME = "AssetAccountTags";
public static final String LIABILITIES_TAG_OPTION_CODE_NAME = "LiabilityAccountTags";
public static final String EQUITY_TAG_OPTION_CODE_NAME = "EquityAccountTags";
public static final String INCOME_TAG_OPTION_CODE_NAME = "IncomeAccountTags";
public static final String EXPENSES_TAG_OPTION_CODE_NAME = "ExpenseAccountTags";
}
@@ -0,0 +1,32 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.common;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
public interface AccountingDropdownReadPlatformService {
public List<EnumOptionData> retrieveGLAccountTypeOptions();
public List<EnumOptionData> retrieveGLAccountUsageOptions();
public List<EnumOptionData> retrieveJournalEntryTypeOptions();
public List<EnumOptionData> retrieveAccountingRuleTypeOptions();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForLoanProducts();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForSavingsProducts();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges();
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions();
}
@@ -0,0 +1,137 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.common;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountingDropdownReadPlatformServiceImpl implements AccountingDropdownReadPlatformService {
private final GLAccountReadPlatformService accountReadPlatformService;
@Autowired
public AccountingDropdownReadPlatformServiceImpl(final GLAccountReadPlatformService accountReadPlatformService) {
this.accountReadPlatformService = accountReadPlatformService;
}
@Override
public List<EnumOptionData> retrieveGLAccountTypeOptions() {
return AccountingEnumerations.gLAccountType(GLAccountType.values());
}
@Override
public List<EnumOptionData> retrieveGLAccountUsageOptions() {
return AccountingEnumerations.gLAccountUsage(GLAccountUsage.values());
}
@Override
public List<EnumOptionData> retrieveJournalEntryTypeOptions() {
return AccountingEnumerations.journalEntryTypes(JournalEntryType.values());
}
@Override
public List<EnumOptionData> retrieveAccountingRuleTypeOptions() {
return AccountingEnumerations.accountingRuleTypes(AccountingRuleType.values());
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForLoanProducts() {
return retrieveAccountMappingOptions();
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForSavingsProducts() {
return retrieveAccountMappingOptions();
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptionsForCharges() {
// only get income and liability accounts
boolean includeIncomeAccounts = true;
boolean includeLiabilityAccounts = true;
boolean includeAssetAccounts = false;
boolean includeExpenseAccounts = false;
boolean includeEquityAccounts = false;
return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts,
includeEquityAccounts);
}
@Override
public Map<String, List<GLAccountData>> retrieveAccountMappingOptions() {
boolean includeAssetAccounts = true;
boolean includeIncomeAccounts = true;
boolean includeExpenseAccounts = true;
boolean includeLiabilityAccounts = true;
boolean includeEquityAccounts = true;
return retrieveAccountMappingOptions(includeAssetAccounts, includeIncomeAccounts, includeExpenseAccounts, includeLiabilityAccounts,
includeEquityAccounts);
}
private Map<String, List<GLAccountData>> retrieveAccountMappingOptions(boolean includeAssetAccounts, boolean includeIncomeAccounts,
boolean includeExpenseAccounts, boolean includeLiabilityAccounts, boolean includeEquityAccounts) {
final Map<String, List<GLAccountData>> accountOptions = new HashMap<>();
if (includeAssetAccounts) {
List<GLAccountData> assetAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.ASSET);
if (assetAccountOptions.isEmpty()) {
assetAccountOptions = null;
}
accountOptions.put("assetAccountOptions", assetAccountOptions);
}
if (includeIncomeAccounts) {
List<GLAccountData> incomeAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.INCOME);
if (incomeAccountOptions.isEmpty()) {
incomeAccountOptions = null;
}
accountOptions.put("incomeAccountOptions", incomeAccountOptions);
}
if (includeExpenseAccounts) {
List<GLAccountData> expenseAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EXPENSE);
if (expenseAccountOptions.isEmpty()) {
expenseAccountOptions = null;
}
accountOptions.put("expenseAccountOptions", expenseAccountOptions);
}
if (includeLiabilityAccounts) {
List<GLAccountData> liabilityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.LIABILITY);
if (liabilityAccountOptions.isEmpty()) {
liabilityAccountOptions = null;
}
accountOptions.put("liabilityAccountOptions", liabilityAccountOptions);
}
if (includeEquityAccounts) {
List<GLAccountData> equityAccountOptions = this.accountReadPlatformService
.retrieveAllEnabledDetailGLAccounts(GLAccountType.EQUITY);
if (equityAccountOptions.isEmpty()) {
equityAccountOptions = null;
}
accountOptions.put("equityAccountOptions", equityAccountOptions);
}
return accountOptions;
}
}
@@ -0,0 +1,100 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.common;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
public class AccountingEnumerations {
public static EnumOptionData gLAccountType(final int id) {
return gLAccountType(GLAccountType.fromInt(id));
}
public static EnumOptionData gLAccountType(final GLAccountType accountType) {
final EnumOptionData optionData = new EnumOptionData(accountType.getValue().longValue(), accountType.getCode(),
accountType.toString());
return optionData;
}
public static List<EnumOptionData> gLAccountType(final GLAccountType[] accountTypes) {
final List<EnumOptionData> optionDatas = new ArrayList<>();
for (final GLAccountType accountType : accountTypes) {
optionDatas.add(gLAccountType(accountType));
}
return optionDatas;
}
public static EnumOptionData gLAccountUsage(final int id) {
return gLAccountUsage(GLAccountUsage.fromInt(id));
}
public static EnumOptionData gLAccountUsage(final GLAccountUsage accountUsage) {
final EnumOptionData optionData = new EnumOptionData(accountUsage.getValue().longValue(), accountUsage.getCode(),
accountUsage.toString());
return optionData;
}
public static List<EnumOptionData> gLAccountUsage(final GLAccountUsage[] accountUsages) {
final List<EnumOptionData> optionDatas = new ArrayList<>();
for (final GLAccountUsage accountUsage : accountUsages) {
optionDatas.add(gLAccountUsage(accountUsage));
}
return optionDatas;
}
public static EnumOptionData journalEntryType(final int id) {
return journalEntryType(JournalEntryType.fromInt(id));
}
public static EnumOptionData journalEntryType(final JournalEntryType journalEntryType) {
final EnumOptionData optionData = new EnumOptionData(journalEntryType.getValue().longValue(), journalEntryType.getCode(),
journalEntryType.toString());
return optionData;
}
public static List<EnumOptionData> journalEntryTypes(final JournalEntryType[] journalEntryTypes) {
final List<EnumOptionData> optionDatas = new ArrayList<>();
for (final JournalEntryType journalEntryType : journalEntryTypes) {
optionDatas.add(journalEntryType(journalEntryType));
}
return optionDatas;
}
public static EnumOptionData portfolioProductType(final int id) {
return portfolioProductType(PortfolioProductType.fromInt(id));
}
public static EnumOptionData portfolioProductType(final PortfolioProductType portfolioProductType) {
final EnumOptionData optionData = new EnumOptionData(portfolioProductType.getValue().longValue(), portfolioProductType.getCode(),
portfolioProductType.toString());
return optionData;
}
public static EnumOptionData accountingRuleType(final int id) {
return accountingRuleType(AccountingRuleType.fromInt(id));
}
public static EnumOptionData accountingRuleType(final AccountingRuleType type) {
final EnumOptionData optionData = new EnumOptionData(type.getValue().longValue(), type.getCode(), type.toString());
return optionData;
}
public static List<EnumOptionData> accountingRuleTypes(final AccountingRuleType[] accountingRuleTypes) {
final List<EnumOptionData> optionDatas = new ArrayList<>();
for (final AccountingRuleType accountingRuleType : accountingRuleTypes) {
optionDatas.add(accountingRuleType(accountingRuleType));
}
return optionDatas;
}
}
@@ -0,0 +1,50 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.common;
import java.util.HashMap;
import java.util.Map;
public enum AccountingRuleType {
NONE(1, "accountingRuleType.none"), //
CASH_BASED(2, "accountingRuleType.cash"), //
ACCRUAL_PERIODIC(3, "accountingRuleType.accrual.periodic"), //
ACCRUAL_UPFRONT(4, "accountingRuleType.accrual.upfront"); //
private final Integer value;
private final String code;
private static final Map<Integer, AccountingRuleType> intToEnumMap = new HashMap<>();
static {
for (final AccountingRuleType type : AccountingRuleType.values()) {
intToEnumMap.put(type.value, type);
}
}
public static AccountingRuleType fromInt(final Integer ruleTypeValue) {
final AccountingRuleType type = intToEnumMap.get(ruleTypeValue);
return type;
}
private AccountingRuleType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
}
@@ -0,0 +1,147 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.api;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.accounting.financialactivityaccount.data.FinancialActivityAccountData;
import org.mifosplatform.accounting.financialactivityaccount.service.FinancialActivityAccountReadPlatformService;
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.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/financialactivityaccounts")
@Component
@Scope("singleton")
public class FinancialActivityAccountsApiResource {
private final FinancialActivityAccountReadPlatformService financialActivityAccountReadPlatformService;
private final DefaultToApiJsonSerializer<FinancialActivityAccountData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public FinancialActivityAccountsApiResource(final PlatformSecurityContext context,
final FinancialActivityAccountReadPlatformService officeToGLAccountMappingReadPlatformService,
final DefaultToApiJsonSerializer<FinancialActivityAccountData> toApiJsonSerializer,
final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.financialActivityAccountReadPlatformService = officeToGLAccountMappingReadPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveTemplate(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
FinancialActivityAccountData financialActivityAccountData = this.financialActivityAccountReadPlatformService
.getFinancialActivityAccountTemplate();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, financialActivityAccountData,
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
final List<FinancialActivityAccountData> financialActivityAccounts = this.financialActivityAccountReadPlatformService.retrieveAll();
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, financialActivityAccounts,
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{mappingId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreive(@PathParam("mappingId") final Long mappingId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(FinancialActivityAccountsConstants.resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
FinancialActivityAccountData financialActivityAccountData = this.financialActivityAccountReadPlatformService.retrieve(mappingId);
if (settings.isTemplate()) {
financialActivityAccountData = this.financialActivityAccountReadPlatformService
.addTemplateDetails(financialActivityAccountData);
}
return this.apiJsonSerializerService.serialize(settings, financialActivityAccountData,
FinancialActivityAccountsConstants.RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLAccount(final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createOfficeToGLAccountMapping().withJson(jsonRequestBody)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@PUT
@Path("{mappingId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateGLAccount(@PathParam("mappingId") final Long mappingId, final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateOfficeToGLAccountMapping(mappingId)
.withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@DELETE
@Path("{mappingId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteGLAccount(@PathParam("mappingId") final Long mappingId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteOfficeToGLAccountMapping(mappingId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.api;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class FinancialActivityAccountsConstants {
public static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "financialActivityData",
"glAccountData", "glAccountOptions", "financialActivityOptions"));
public static final String resourceNameForPermission = "FINANCIALACTIVITYACCOUNT";
}
@@ -0,0 +1,42 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a GL Account
***/
public enum FinancialActivityAccountsJsonInputParams {
FINANCIAL_ACTIVITY_ID("financialActivityId"), GL_ACCOUNT_ID("glAccountId");
private final String value;
private FinancialActivityAccountsJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final FinancialActivityAccountsJsonInputParams type : FinancialActivityAccountsJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,73 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.data;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
public class FinancialActivityAccountData {
private final Long id;
private final FinancialActivityData financialActivityData;
private final GLAccountData glAccountData;
private Map<String, List<GLAccountData>> glAccountOptions;
private List<FinancialActivityData> financialActivityOptions;
public FinancialActivityAccountData() {
this.id = null;
this.glAccountData = null;
this.financialActivityData = null;
this.glAccountOptions = null;
this.financialActivityOptions = null;
}
public FinancialActivityAccountData(final Long id, final FinancialActivityData financialActivityData, final GLAccountData glAccountData) {
this.id = id;
this.glAccountData = glAccountData;
this.financialActivityData = financialActivityData;
}
public FinancialActivityAccountData(Map<String, List<GLAccountData>> glAccountOptions,
List<FinancialActivityData> financialActivityOptions) {
this.id = null;
this.glAccountData = null;
this.financialActivityData = null;
this.glAccountOptions = glAccountOptions;
this.financialActivityOptions = financialActivityOptions;
}
public List<FinancialActivityData> getFinancialActivityOptions() {
return financialActivityOptions;
}
public void setFinancialActivityOptions(List<FinancialActivityData> financialActivityOptions) {
this.financialActivityOptions = financialActivityOptions;
}
public Map<String, List<GLAccountData>> getAccountingMappingOptions() {
return this.glAccountOptions;
}
public void setAccountingMappingOptions(Map<String, List<GLAccountData>> accountingMappingOptions) {
this.glAccountOptions = accountingMappingOptions;
}
public GLAccountData getGlAccountData() {
return glAccountData;
}
public FinancialActivityData getFinancialActivityData() {
return financialActivityData;
}
public Long getId() {
return id;
}
}
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.data;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
public class FinancialActivityData {
private final Integer id;
private final String name;
private final GLAccountType mappedGLAccountType;
public Integer getId() {
return this.id;
}
public String getName() {
return this.name;
}
public GLAccountType getMappedGLAccountType() {
return this.mappedGLAccountType;
}
public FinancialActivityData(Integer id, String name, GLAccountType mappedGLAccountType) {
super();
this.id = id;
this.name = name;
this.mappedGLAccountType = mappedGLAccountType;
}
}
@@ -0,0 +1,57 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "acc_gl_financial_activity_account")
public class FinancialActivityAccount extends AbstractPersistable<Long> {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "gl_account_id")
private GLAccount glAccount;
@Column(name = "financial_activity_type", nullable = false)
private Integer financialActivityType;
public static FinancialActivityAccount createNew(final GLAccount glAccount, final Integer financialAccountType) {
return new FinancialActivityAccount(glAccount, financialAccountType);
}
protected FinancialActivityAccount() {
//
}
private FinancialActivityAccount(final GLAccount glAccount, final int financialAccountType) {
this.glAccount = glAccount;
this.financialActivityType = financialAccountType;
}
public GLAccount getGlAccount() {
return this.glAccount;
}
public Integer getFinancialActivityType() {
return this.financialActivityType;
}
public void updateGlAccount(final GLAccount glAccount) {
this.glAccount = glAccount;
}
public void updateFinancialActivityType(final Integer financialActivityType) {
this.financialActivityType = financialActivityType;
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface FinancialActivityAccountRepository extends JpaRepository<FinancialActivityAccount, Long>,
JpaSpecificationExecutor<FinancialActivityAccount> {
@Query("from FinancialActivityAccount faa where faa.financialActivityType = :financialActivityType")
FinancialActivityAccount findByFinancialActivityType(@Param("financialActivityType") int financialAccountType);
}
@@ -0,0 +1,57 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.domain;
import java.util.List;
import org.mifosplatform.accounting.financialactivityaccount.exception.FinancialActivityAccountNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link FinancialActivityAccountRepository} that adds NULL
* checking and Error handling capabilities
* </p>
*/
@Service
public class FinancialActivityAccountRepositoryWrapper {
private final FinancialActivityAccountRepository repository;
@Autowired
public FinancialActivityAccountRepositoryWrapper(final FinancialActivityAccountRepository repository) {
this.repository = repository;
}
public FinancialActivityAccount findOneWithNotFoundDetection(final Long id) {
final FinancialActivityAccount financialActivityAccount = this.repository.findOne(id);
if (financialActivityAccount == null) { throw new FinancialActivityAccountNotFoundException(id); }
return financialActivityAccount;
}
public FinancialActivityAccount findByFinancialActivityTypeWithNotFoundDetection(final int financialActivityType) {
FinancialActivityAccount financialActivityAccount = this.repository.findByFinancialActivityType(financialActivityType);
if (financialActivityAccount == null) { throw new FinancialActivityAccountNotFoundException(financialActivityType); }
return financialActivityAccount;
}
public List<FinancialActivityAccount> findAll() {
return this.repository.findAll();
}
public void save(final FinancialActivityAccount entity) {
this.repository.save(entity);
}
public void saveAndFlush(final FinancialActivityAccount entity) {
this.repository.saveAndFlush(entity);
}
public void delete(final FinancialActivityAccount entity) {
this.repository.delete(entity);
}
}
@@ -0,0 +1,26 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when product to GL account mapping are not
* found.
*/
public class DuplicateFinancialActivityAccountFoundException extends AbstractPlatformDomainRuleException {
private final static String errorCode = "error.msg.financialActivityAccount.exists";
public DuplicateFinancialActivityAccountFoundException(final Integer financialActivityType) {
super(errorCode, "Mapping for activity already exists " + financialActivityType, financialActivityType);
}
public static String getErrorcode() {
return errorCode;
}
}
@@ -0,0 +1,31 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.exception;
import org.mifosplatform.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when product to GL account mapping are not
* found.
*/
public class FinancialActivityAccountInvalidException extends AbstractPlatformDomainRuleException {
private final static String errorCode = "error.msg.financialActivityAccount.invalid";
public FinancialActivityAccountInvalidException(final FINANCIAL_ACTIVITY financialActivity, final GLAccount glAccount) {
super(errorCode, "Financial Activity '" + financialActivity.getCode() + "' with Id :" + financialActivity.getValue()
+ "' can only be associated with a Ledger Account of Type " + financialActivity.getMappedGLAccountType().getCode()
+ " the provided Ledger Account '" + glAccount.getName() + "(" + glAccount.getGlCode()
+ ")' does not of the required type", financialActivity.getCode(), financialActivity.getValue(), financialActivity
.getMappedGLAccountType().getCode(), glAccount.getName(), glAccount.getGlCode());
}
public static String getErrorcode() {
return errorCode;
}
}
@@ -0,0 +1,25 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when product to GL account mapping are not
* found.
*/
public class FinancialActivityAccountNotFoundException extends AbstractPlatformResourceNotFoundException {
public FinancialActivityAccountNotFoundException(final Long id) {
super("error.msg.financialActivityAccount.not.found", "Financial Activity account with Id " + id + " does not exist", id);
}
public FinancialActivityAccountNotFoundException(final Integer financialActivityType) {
super("error.msg.financialActivityAccount.not.found", "Financial Activity account with for the financial Activity with Id "
+ financialActivityType + " does not exist", financialActivityType);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.handler;
import org.mifosplatform.accounting.financialactivityaccount.service.FinancialActivityAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "FINANCIALACTIVITYACCOUNT", action = "CREATE")
public class CreateFinancialActivityAccountHandler implements NewCommandSourceHandler {
private final FinancialActivityAccountWritePlatformService writePlatformService;
@Autowired
public CreateFinancialActivityAccountHandler(final FinancialActivityAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createFinancialActivityAccountMapping(command);
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.handler;
import org.mifosplatform.accounting.financialactivityaccount.service.FinancialActivityAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "FINANCIALACTIVITYACCOUNT", action = "DELETE")
public class DeleteFinancialActivityAccountCommandHandler implements NewCommandSourceHandler {
private final FinancialActivityAccountWritePlatformService writePlatformService;
@Autowired
public DeleteFinancialActivityAccountCommandHandler(final FinancialActivityAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.deleteGLAccountActivityMapping(command.entityId(), command);
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.handler;
import org.mifosplatform.accounting.financialactivityaccount.service.FinancialActivityAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "FINANCIALACTIVITYACCOUNT", action = "UPDATE")
public class UpdateFinancialActivityAccountCommandHandler implements NewCommandSourceHandler {
private final FinancialActivityAccountWritePlatformService writePlatformService;
@Autowired
public UpdateFinancialActivityAccountCommandHandler(final FinancialActivityAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.updateGLAccountActivityMapping(command.entityId(), command);
}
}
@@ -0,0 +1,107 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.serialization;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
import org.mifosplatform.accounting.financialactivityaccount.api.FinancialActivityAccountsJsonInputParams;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
@Component
public final class FinancialActivityAccountDataValidator {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = FinancialActivityAccountsJsonInputParams.getAllValues();
private final FromJsonHelper fromApiJsonHelper;
private final String paramNameForFinancialActivity = FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue();
private final String paramNameForGLAccount = FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue();
@Autowired
public FinancialActivityAccountDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForCreate(final String json) {
validateJSONAndCheckForUnsupportedParams(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = getDataValidator(dataValidationErrors);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Integer financialActivityId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(paramNameForFinancialActivity, element);
baseDataValidator.reset().parameter(paramNameForFinancialActivity).value(financialActivityId).notNull().isOneOfTheseValues(
FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(),
FINANCIAL_ACTIVITY.CASH_AT_MAINVAULT.getValue(), FINANCIAL_ACTIVITY.CASH_AT_TELLER.getValue(),
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue());
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(paramNameForGLAccount, element);
baseDataValidator.reset().parameter(paramNameForGLAccount).value(glAccountId).notNull().integerGreaterThanZero();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private DataValidatorBuilder getDataValidator(final List<ApiParameterError> dataValidationErrors) {
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("financialactivityaccount");
return baseDataValidator;
}
public void validateForUpdate(final String json) {
validateJSONAndCheckForUnsupportedParams(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = getDataValidator(dataValidationErrors);
final JsonElement element = this.fromApiJsonHelper.parse(json);
if (this.fromApiJsonHelper.parameterExists(paramNameForFinancialActivity, element)) {
final Integer financialActivityId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(paramNameForFinancialActivity,
element);
baseDataValidator.reset().parameter(paramNameForFinancialActivity).value(financialActivityId).ignoreIfNull().isOneOfTheseValues(
FINANCIAL_ACTIVITY.ASSET_TRANSFER.getValue(), FINANCIAL_ACTIVITY.LIABILITY_TRANSFER.getValue(),
FINANCIAL_ACTIVITY.OPENING_BALANCES_TRANSFER_CONTRA.getValue(), FINANCIAL_ACTIVITY.ASSET_FUND_SOURCE.getValue());
}
if (this.fromApiJsonHelper.parameterExists(paramNameForGLAccount, element)) {
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed(paramNameForGLAccount, element);
baseDataValidator.reset().parameter(paramNameForGLAccount).value(glAccountId).ignoreIfNull().integerGreaterThanZero();
}
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void validateJSONAndCheckForUnsupportedParams(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -0,0 +1,22 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.service;
import java.util.List;
import org.mifosplatform.accounting.financialactivityaccount.data.FinancialActivityAccountData;
public interface FinancialActivityAccountReadPlatformService {
List<FinancialActivityAccountData> retrieveAll();
FinancialActivityAccountData retrieve(Long mappingId);
FinancialActivityAccountData addTemplateDetails(FinancialActivityAccountData financialActivityAccountData);
FinancialActivityAccountData getFinancialActivityAccountTemplate();
}
@@ -0,0 +1,109 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
import org.mifosplatform.accounting.common.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.financialactivityaccount.data.FinancialActivityAccountData;
import org.mifosplatform.accounting.financialactivityaccount.data.FinancialActivityData;
import org.mifosplatform.accounting.financialactivityaccount.exception.FinancialActivityAccountNotFoundException;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
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 FinancialActivityAccountReadPlatformServiceImpl implements FinancialActivityAccountReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final FinancialActivityAccountMapper financialActivityAccountMapper;
private final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService;
@Autowired
public FinancialActivityAccountReadPlatformServiceImpl(final RoutingDataSource dataSource,
final AccountingDropdownReadPlatformService accountingDropdownReadPlatformService) {
financialActivityAccountMapper = new FinancialActivityAccountMapper();
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.accountingDropdownReadPlatformService = accountingDropdownReadPlatformService;
}
@Override
public List<FinancialActivityAccountData> retrieveAll() {
String sql = "select " + financialActivityAccountMapper.schema();
return this.jdbcTemplate.query(sql, financialActivityAccountMapper, new Object[] {});
}
@Override
public FinancialActivityAccountData retrieve(Long financialActivityAccountId) {
try {
StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select ");
sqlBuilder.append(this.financialActivityAccountMapper.schema());
sqlBuilder.append(" where faa.id=?");
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), this.financialActivityAccountMapper,
new Object[] { financialActivityAccountId });
} catch (final EmptyResultDataAccessException e) {
throw new FinancialActivityAccountNotFoundException(financialActivityAccountId);
}
}
@Override
public FinancialActivityAccountData addTemplateDetails(FinancialActivityAccountData financialActivityAccountData) {
final Map<String, List<GLAccountData>> accountOptions = this.accountingDropdownReadPlatformService.retrieveAccountMappingOptions();
financialActivityAccountData.setAccountingMappingOptions(accountOptions);
financialActivityAccountData.setFinancialActivityOptions(FINANCIAL_ACTIVITY.getAllFinancialActivities());
return financialActivityAccountData;
}
@Override
public FinancialActivityAccountData getFinancialActivityAccountTemplate() {
FinancialActivityAccountData financialActivityAccountData = new FinancialActivityAccountData();
return addTemplateDetails(financialActivityAccountData);
}
private static final class FinancialActivityAccountMapper implements RowMapper<FinancialActivityAccountData> {
private final String sql;
public FinancialActivityAccountMapper() {
StringBuilder sb = new StringBuilder(300);
sb.append(" faa.id as id, faa.financial_activity_type as financialActivityId, glaccount.id as glAccountId,glaccount.name as glAccountName,glaccount.gl_code as glCode ");
sb.append(" from acc_gl_financial_activity_account faa ");
sb.append(" join acc_gl_account glaccount on glaccount.id = faa.gl_account_id");
sql = sb.toString();
}
public String schema() {
return sql;
}
@Override
public FinancialActivityAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final Long glAccountId = JdbcSupport.getLong(rs, "glAccountId");
final Integer financialActivityId = JdbcSupport.getInteger(rs, "financialActivityId");
final String glAccountName = rs.getString("glAccountName");
final String glCode = rs.getString("glCode");
final GLAccountData glAccountData = new GLAccountData(glAccountId, glAccountName, glCode);
final FinancialActivityData financialActivityData = FINANCIAL_ACTIVITY.toFinancialActivityData(financialActivityId);
final FinancialActivityAccountData financialActivityAccountData = new FinancialActivityAccountData(id, financialActivityData,
glAccountData);
return financialActivityAccountData;
}
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface FinancialActivityAccountWritePlatformService {
CommandProcessingResult createFinancialActivityAccountMapping(JsonCommand command);
CommandProcessingResult updateGLAccountActivityMapping(Long mappingId, JsonCommand command);
CommandProcessingResult deleteGLAccountActivityMapping(Long mappingId, JsonCommand command);
}
@@ -0,0 +1,163 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.financialactivityaccount.service;
import java.util.HashMap;
import java.util.Map;
import org.mifosplatform.accounting.common.AccountingConstants.FINANCIAL_ACTIVITY;
import org.mifosplatform.accounting.financialactivityaccount.api.FinancialActivityAccountsJsonInputParams;
import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccount;
import org.mifosplatform.accounting.financialactivityaccount.domain.FinancialActivityAccountRepositoryWrapper;
import org.mifosplatform.accounting.financialactivityaccount.exception.DuplicateFinancialActivityAccountFoundException;
import org.mifosplatform.accounting.financialactivityaccount.exception.FinancialActivityAccountInvalidException;
import org.mifosplatform.accounting.financialactivityaccount.serialization.FinancialActivityAccountDataValidator;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper;
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.core.exception.PlatformDataIntegrityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
@Service
public class FinancialActivityAccountWritePlatformServiceImpl implements FinancialActivityAccountWritePlatformService {
private final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository;
private final FinancialActivityAccountDataValidator fromApiJsonDeserializer;
private final GLAccountRepositoryWrapper glAccountRepositoryWrapper;
private final static Logger logger = LoggerFactory.getLogger(FinancialActivityAccountWritePlatformServiceImpl.class);
@Autowired
public FinancialActivityAccountWritePlatformServiceImpl(
final FinancialActivityAccountRepositoryWrapper financialActivityAccountRepository,
final FinancialActivityAccountDataValidator fromApiJsonDeserializer, final GLAccountRepositoryWrapper glAccountRepositoryWrapper) {
this.financialActivityAccountRepository = financialActivityAccountRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.glAccountRepositoryWrapper = glAccountRepositoryWrapper;
}
@Override
public CommandProcessingResult createFinancialActivityAccountMapping(JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForCreate(command.json());
final Integer financialActivityId = command
.integerValueSansLocaleOfParameterNamed(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue());
final Long accountId = command.longValueOfParameterNamed(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue());
final GLAccount glAccount = glAccountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
FinancialActivityAccount financialActivityAccount = FinancialActivityAccount.createNew(glAccount, financialActivityId);
validateFinancialActivityAndAccountMapping(financialActivityAccount);
this.financialActivityAccountRepository.save(financialActivityAccount);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(financialActivityAccount.getId()) //
.build();
} catch (DataIntegrityViolationException dataIntegrityViolationException) {
handleFinancialActivityAccountDataIntegrityIssues(command, dataIntegrityViolationException);
return CommandProcessingResult.empty();
}
}
/**
* Validate that the GL Account is appropriate for the particular Financial
* Activity Type
**/
private void validateFinancialActivityAndAccountMapping(FinancialActivityAccount financialActivityAccount) {
FINANCIAL_ACTIVITY financialActivity = FINANCIAL_ACTIVITY.fromInt(financialActivityAccount.getFinancialActivityType());
GLAccount glAccount = financialActivityAccount.getGlAccount();
if (!financialActivity.getMappedGLAccountType().getValue().equals(glAccount.getType())) { throw new FinancialActivityAccountInvalidException(
financialActivity, glAccount); }
}
@Override
public CommandProcessingResult updateGLAccountActivityMapping(Long financialActivityAccountId, JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
final FinancialActivityAccount financialActivityAccount = this.financialActivityAccountRepository
.findOneWithNotFoundDetection(financialActivityAccountId);
Map<String, Object> changes = findChanges(command, financialActivityAccount);
if (changes.containsKey(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue())) {
final Long accountId = command.longValueOfParameterNamed(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue());
final GLAccount glAccount = glAccountRepositoryWrapper.findOneWithNotFoundDetection(accountId);
financialActivityAccount.updateGlAccount(glAccount);
}
if (changes.containsKey(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue())) {
final Integer financialActivityId = command
.integerValueSansLocaleOfParameterNamed(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue());
financialActivityAccount.updateFinancialActivityType(financialActivityId);
}
if (!changes.isEmpty()) {
validateFinancialActivityAndAccountMapping(financialActivityAccount);
this.financialActivityAccountRepository.save(financialActivityAccount);
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(financialActivityAccountId) //
.with(changes) //
.build();
} catch (DataIntegrityViolationException dataIntegrityViolationException) {
handleFinancialActivityAccountDataIntegrityIssues(command, dataIntegrityViolationException);
return CommandProcessingResult.empty();
}
}
@Override
public CommandProcessingResult deleteGLAccountActivityMapping(Long financialActivityAccountId, JsonCommand command) {
final FinancialActivityAccount financialActivityAccount = this.financialActivityAccountRepository
.findOneWithNotFoundDetection(financialActivityAccountId);
this.financialActivityAccountRepository.delete(financialActivityAccount);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(financialActivityAccountId) //
.build();
}
private void handleFinancialActivityAccountDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("financial_activity_type")) {
final Integer financialActivityId = command
.integerValueSansLocaleOfParameterNamed(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue());
throw new DuplicateFinancialActivityAccountFoundException(financialActivityId);
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.glAccount.unknown.data.integrity.issue",
"Unknown data integrity issue with resource GL Account: " + realCause.getMessage());
}
public Map<String, Object> findChanges(JsonCommand command, FinancialActivityAccount financialActivityAccount) {
Map<String, Object> changes = new HashMap<>();
Long existingGLAccountId = financialActivityAccount.getGlAccount().getId();
Integer financialActivityType = financialActivityAccount.getFinancialActivityType();
// is the account Id changed?
if (command.isChangeInLongParameterNamed(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue(), existingGLAccountId)) {
final Long newValue = command.longValueOfParameterNamed(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue());
changes.put(FinancialActivityAccountsJsonInputParams.GL_ACCOUNT_ID.getValue(), newValue);
}
// is the financial Activity changed
if (command.isChangeInIntegerSansLocaleParameterNamed(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue(),
financialActivityType)) {
final Integer newValue = command
.integerValueSansLocaleOfParameterNamed(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue());
changes.put(FinancialActivityAccountsJsonInputParams.FINANCIAL_ACTIVITY_ID.getValue(), newValue);
}
return changes;
}
}
@@ -0,0 +1,43 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a GL Account
***/
public enum GLAccountJsonInputParams {
ID("id"), NAME("name"), PARENT_ID("parentId"), GL_CODE("glCode"), DISABLED("disabled"), MANUAL_ENTRIES_ALLOWED("manualEntriesAllowed"), TYPE(
"type"), USAGE("usage"), DESCRIPTION("description"), TAGID("tagId");
private final String value;
private GLAccountJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final GLAccountJsonInputParams type : GLAccountJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,209 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.api;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
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.accounting.common.AccountingConstants;
import org.mifosplatform.accounting.common.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
import org.mifosplatform.accounting.journalentry.data.JournalEntryAssociationParametersData;
import org.mifosplatform.commands.domain.CommandWrapper;
import org.mifosplatform.commands.service.CommandWrapperBuilder;
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.codes.service.CodeValueReadPlatformService;
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/glaccounts")
@Component
@Scope("singleton")
public class GLAccountsApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "name", "parentId", "glCode",
"disabled", "manualEntriesAllowed", "type", "usage", "description", "assetHeaderAccountOptions",
"liabilityHeaderAccountOptions", "equityHeaderAccountOptions", "incomeHeaderAccountOptions", "expenseHeaderAccountOptions",
"nameDecorated", "tagId", "allowedAssetsTagOptions", "allowedLiabilitiesTagOptions", "allowedEquityTagOptions",
"allowedIncomeTagOptions", "allowedExpensesTagOptions", "creditAccounts", "debitAccounts"));
private final String resourceNameForPermission = "GLACCOUNT";
private final GLAccountReadPlatformService glAccountReadPlatformService;
private final AccountingDropdownReadPlatformService dropdownReadPlatformService;
private final DefaultToApiJsonSerializer<GLAccountData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
@Autowired
public GLAccountsApiResource(final PlatformSecurityContext context, final GLAccountReadPlatformService glAccountReadPlatformService,
final DefaultToApiJsonSerializer<GLAccountData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final AccountingDropdownReadPlatformService dropdownReadPlatformService,
final CodeValueReadPlatformService codeValueReadPlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.glAccountReadPlatformService = glAccountReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
this.codeValueReadPlatformService = codeValueReadPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveNewAccountDetails(@Context final UriInfo uriInfo, @QueryParam("type") final Integer type) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveNewGLAccountDetails(type);
glAccountData = handleTemplate(glAccountData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glAccountData, RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllAccounts(@Context final UriInfo uriInfo, @QueryParam("type") final Integer type,
@QueryParam("searchParam") final String searchParam, @QueryParam("usage") final Integer usage,
@QueryParam("manualEntriesAllowed") final Boolean manualEntriesAllowed, @QueryParam("disabled") final Boolean disabled,
@QueryParam("fetchRunningBalance") final boolean runningBalance) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance);
final List<GLAccountData> glAccountDatas = this.glAccountReadPlatformService.retrieveAllGLAccounts(type, searchParam, usage,
manualEntriesAllowed, disabled, associationParametersData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glAccountDatas, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveAccount(@PathParam("glAccountId") final Long glAccountId, @Context final UriInfo uriInfo,
@QueryParam("fetchRunningBalance") final boolean runningBalance) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(false, runningBalance);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId, associationParametersData);
if (settings.isTemplate()) {
glAccountData = handleTemplate(glAccountData);
}
return this.apiJsonSerializerService.serialize(settings, glAccountData, RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLAccount(final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createGLAccount().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@PUT
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateGLAccount(@PathParam("glAccountId") final Long glAccountId, final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateGLAccount(glAccountId).withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@DELETE
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String deleteGLAccount(@PathParam("glAccountId") final Long glAccountId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteGLAccount(glAccountId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
private GLAccountData handleTemplate(final GLAccountData glAccountData) {
final List<EnumOptionData> accountTypeOptions = this.dropdownReadPlatformService.retrieveGLAccountTypeOptions();
final List<EnumOptionData> usageOptions = this.dropdownReadPlatformService.retrieveGLAccountUsageOptions();
final List<GLAccountData> assetHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.ASSET));
final List<GLAccountData> liabilityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.LIABILITY));
final List<GLAccountData> equityHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EQUITY));
final List<GLAccountData> incomeHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.INCOME));
final List<GLAccountData> expenseHeaderAccountOptions = defaultIfEmpty(this.glAccountReadPlatformService
.retrieveAllEnabledHeaderGLAccounts(GLAccountType.EXPENSE));
final Collection<CodeValueData> allowedAssetsTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
final Collection<CodeValueData> allowedLiabilitiesTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME);
final Collection<CodeValueData> allowedEquityTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME);
final Collection<CodeValueData> allowedIncomeTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME);
final Collection<CodeValueData> allowedExpensesTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME);
return new GLAccountData(glAccountData, accountTypeOptions, usageOptions, assetHeaderAccountOptions, liabilityHeaderAccountOptions,
equityHeaderAccountOptions, incomeHeaderAccountOptions, expenseHeaderAccountOptions, allowedAssetsTagOptions,
allowedLiabilitiesTagOptions, allowedEquityTagOptions, allowedIncomeTagOptions, allowedExpensesTagOptions);
}
private List<GLAccountData> defaultIfEmpty(final List<GLAccountData> list) {
List<GLAccountData> returnList = null;
if (list != null && !list.isEmpty()) {
returnList = list;
}
return returnList;
}
}
@@ -0,0 +1,118 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.command;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.glaccount.api.GLAccountJsonInputParams;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
/**
* Immutable command for adding a general Ledger Account
*/
public class GLAccountCommand {
@SuppressWarnings("unused")
private final Long id;
private final String name;
private final Long parentId;
private final String glCode;
private final Boolean disabled;
private final Boolean manualEntriesAllowed;
private final Integer usage;
private final Integer type;
private final String description;
private final Long tagId;
public GLAccountCommand(final Long id, final String name, final Long parentId, final String glCode, final Boolean disabled,
final Boolean manualEntriesAllowed, final Integer type, final Integer usage, final String description, final Long tagId) {
this.id = id;
this.name = name;
this.parentId = parentId;
this.glCode = glCode;
this.disabled = disabled;
this.manualEntriesAllowed = manualEntriesAllowed;
this.type = type;
this.usage = usage;
this.description = description;
this.tagId = tagId;
}
public void validateForCreate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).notBlank().notExceedingLengthOf(200);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.GL_CODE.getValue()).value(this.glCode).notBlank()
.notExceedingLengthOf(45);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.PARENT_ID.getValue()).value(this.parentId).ignoreIfNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(GLAccountJsonInputParams.TYPE.getValue()).value(this.type).notNull()
.inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue());
baseDataValidator.reset().parameter(GLAccountJsonInputParams.USAGE.getValue()).value(this.usage)
.inMinMaxRange(GLAccountUsage.getMinValue(), GLAccountUsage.getMaxValue());
baseDataValidator.reset().parameter(GLAccountJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull()
.notExceedingLengthOf(500);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED.getValue()).value(this.manualEntriesAllowed)
.notBlank();
baseDataValidator.reset().parameter(GLAccountJsonInputParams.TAGID.getValue()).value(this.tagId).ignoreIfNull()
.longGreaterThanZero();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).ignoreIfNull().notBlank()
.notExceedingLengthOf(200);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.GL_CODE.getValue()).ignoreIfNull().value(this.glCode).notBlank()
.notExceedingLengthOf(45);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.PARENT_ID.getValue()).value(this.parentId).ignoreIfNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(GLAccountJsonInputParams.TYPE.getValue()).value(this.type).ignoreIfNull()
.inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue());
baseDataValidator.reset().parameter(GLAccountJsonInputParams.USAGE.getValue()).value(this.usage).ignoreIfNull()
.inMinMaxRange(GLAccountUsage.getMinValue(), GLAccountUsage.getMaxValue());
baseDataValidator.reset().parameter(GLAccountJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull()
.notBlank().notExceedingLengthOf(500);
baseDataValidator.reset().parameter(GLAccountJsonInputParams.DISABLED.getValue()).value(this.disabled).ignoreIfNull();
baseDataValidator.reset().anyOfNotNull(this.name, this.glCode, this.parentId, this.type, this.description, this.disabled);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
baseDataValidator.reset().parameter(GLAccountJsonInputParams.TAGID.getValue()).value(this.tagId).ignoreIfNull()
.longGreaterThanZero();
}
public boolean isHeaderAccount() {
return GLAccountUsage.HEADER.getValue().equals(this.usage);
}
}
@@ -0,0 +1,185 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.data;
import java.util.Collection;
import java.util.List;
import org.mifosplatform.accounting.common.AccountingEnumerations;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
/**
* Immutable object representing a General Ledger Account
*
* Note: no getter/setters required as google-gson will produce json from fields
* of object.
*/
public class GLAccountData {
private final Long id;
private final String name;
private final Long parentId;
private final String glCode;
private final Boolean disabled;
private final Boolean manualEntriesAllowed;
private final EnumOptionData type;
private final EnumOptionData usage;
private final String description;
private final String nameDecorated;
private final CodeValueData tagId;
private final Long organizationRunningBalance;
// templates
final List<EnumOptionData> accountTypeOptions;
final List<EnumOptionData> usageOptions;
final List<GLAccountData> assetHeaderAccountOptions;
final List<GLAccountData> liabilityHeaderAccountOptions;
final List<GLAccountData> equityHeaderAccountOptions;
final List<GLAccountData> incomeHeaderAccountOptions;
final List<GLAccountData> expenseHeaderAccountOptions;
final Collection<CodeValueData> allowedAssetsTagOptions;
final Collection<CodeValueData> allowedLiabilitiesTagOptions;
final Collection<CodeValueData> allowedEquityTagOptions;
final Collection<CodeValueData> allowedIncomeTagOptions;
final Collection<CodeValueData> allowedExpensesTagOptions;
public GLAccountData(final Long id, final String name, final Long parentId, final String glCode, final boolean disabled,
final boolean manualEntriesAllowed, final EnumOptionData type, final EnumOptionData usage, final String description,
final String nameDecorated, final CodeValueData tagId, final Long organizationRunningBalance) {
this.id = id;
this.name = name;
this.parentId = parentId;
this.glCode = glCode;
this.disabled = disabled;
this.manualEntriesAllowed = manualEntriesAllowed;
this.type = type;
this.usage = usage;
this.description = description;
this.nameDecorated = nameDecorated;
this.tagId = tagId;
this.organizationRunningBalance = organizationRunningBalance;
this.accountTypeOptions = null;
this.usageOptions = null;
this.assetHeaderAccountOptions = null;
this.liabilityHeaderAccountOptions = null;
this.equityHeaderAccountOptions = null;
this.incomeHeaderAccountOptions = null;
this.expenseHeaderAccountOptions = null;
this.allowedAssetsTagOptions = null;
this.allowedLiabilitiesTagOptions = null;
this.allowedEquityTagOptions = null;
this.allowedIncomeTagOptions = null;
this.allowedExpensesTagOptions = null;
}
public GLAccountData(final GLAccountData accountData, final List<EnumOptionData> accountTypeOptions,
final List<EnumOptionData> usageOptions, final List<GLAccountData> assetHeaderAccountOptions,
final List<GLAccountData> liabilityHeaderAccountOptions, final List<GLAccountData> equityHeaderAccountOptions,
final List<GLAccountData> incomeHeaderAccountOptions, final List<GLAccountData> expenseHeaderAccountOptions,
final Collection<CodeValueData> allowedAssetsTagOptions, final Collection<CodeValueData> allowedLiabilitiesTagOptions,
final Collection<CodeValueData> allowedEquityTagOptions, final Collection<CodeValueData> allowedIncomeTagOptions,
final Collection<CodeValueData> allowedExpensesTagOptions) {
this.id = accountData.id;
this.name = accountData.name;
this.parentId = accountData.parentId;
this.glCode = accountData.glCode;
this.disabled = accountData.disabled;
this.manualEntriesAllowed = accountData.manualEntriesAllowed;
this.type = accountData.type;
this.usage = accountData.usage;
this.description = accountData.description;
this.nameDecorated = accountData.nameDecorated;
this.tagId = accountData.tagId;
this.organizationRunningBalance = accountData.organizationRunningBalance;
this.accountTypeOptions = accountTypeOptions;
this.usageOptions = usageOptions;
this.assetHeaderAccountOptions = assetHeaderAccountOptions;
this.liabilityHeaderAccountOptions = liabilityHeaderAccountOptions;
this.equityHeaderAccountOptions = equityHeaderAccountOptions;
this.incomeHeaderAccountOptions = incomeHeaderAccountOptions;
this.expenseHeaderAccountOptions = expenseHeaderAccountOptions;
this.allowedAssetsTagOptions = allowedAssetsTagOptions;
this.allowedLiabilitiesTagOptions = allowedLiabilitiesTagOptions;
this.allowedEquityTagOptions = allowedEquityTagOptions;
this.allowedIncomeTagOptions = allowedIncomeTagOptions;
this.allowedExpensesTagOptions = allowedExpensesTagOptions;
}
public static GLAccountData sensibleDefaultsForNewGLAccountCreation(final Integer glAccType) {
final Long id = null;
final String name = null;
final Long parentId = null;
final String glCode = null;
final boolean disabled = false;
final boolean manualEntriesAllowed = true;
final EnumOptionData type;
if (glAccType != null && glAccType >= GLAccountType.getMinValue() && glAccType <= GLAccountType.getMaxValue()) {
type = AccountingEnumerations.gLAccountType(glAccType);
} else {
type = AccountingEnumerations.gLAccountType(GLAccountType.ASSET);
}
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(GLAccountUsage.DETAIL);
final String description = null;
final String nameDecorated = null;
final CodeValueData tagId = null;
final Long organizationRunningBalance = null;
return new GLAccountData(id, name, parentId, glCode, disabled, manualEntriesAllowed, type, usage, description, nameDecorated,
tagId, organizationRunningBalance);
}
public GLAccountData(final Long id, final String name, final String glCode) {
this.id = id;
this.name = name;
this.parentId = null;
this.glCode = glCode;
this.disabled = null;
this.manualEntriesAllowed = null;
this.type = null;
this.usage = null;
this.description = null;
this.nameDecorated = null;
this.tagId = null;
this.organizationRunningBalance = null;
this.accountTypeOptions = null;
this.usageOptions = null;
this.assetHeaderAccountOptions = null;
this.liabilityHeaderAccountOptions = null;
this.equityHeaderAccountOptions = null;
this.incomeHeaderAccountOptions = null;
this.expenseHeaderAccountOptions = null;
this.allowedAssetsTagOptions = null;
this.allowedLiabilitiesTagOptions = null;
this.allowedEquityTagOptions = null;
this.allowedIncomeTagOptions = null;
this.allowedExpensesTagOptions = null;
}
public Long getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getGlCode() {
return this.glCode;
}
public EnumOptionData getType() {
return this.type;
}
public Integer getTypeId() {
if (this.type != null) { return this.type.getId().intValue(); }
return null;
}
}
@@ -0,0 +1,26 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.data;
public class GLAccountDataForLookup {
private final Long id;
@SuppressWarnings("unused")
private final String name;
@SuppressWarnings("unused")
private final String glCode;
public GLAccountDataForLookup(final Long id, final String name, final String glCode) {
this.id = id;
this.name = name;
this.glCode = glCode;
}
public Long getId() {
return this.id;
}
}
@@ -0,0 +1,237 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.glaccount.api.GLAccountJsonInputParams;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@Table(name = "acc_gl_account", uniqueConstraints = { @UniqueConstraint(columnNames = { "gl_code" }, name = "acc_gl_code") })
public class GLAccount extends AbstractPersistable<Long> {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private GLAccount parent;
@Column(name = "hierarchy", nullable = true, length = 50)
private String hierarchy;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private final List<GLAccount> children = new LinkedList<>();
@Column(name = "name", nullable = false, length = 45)
private String name;
@Column(name = "gl_code", nullable = false, length = 100)
private String glCode;
@Column(name = "disabled", nullable = false)
private boolean disabled = false;
@Column(name = "manual_journal_entries_allowed", nullable = false)
private boolean manualEntriesAllowed = true;
@Column(name = "classification_enum", nullable = false)
private Integer type;
@Column(name = "account_usage", nullable = false)
private Integer usage;
@Column(name = "description", nullable = true, length = 500)
private String description;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tag_id")
private CodeValue tagId;
protected GLAccount() {
//
}
private GLAccount(final GLAccount parent, final String name, final String glCode, final boolean disabled,
final boolean manualEntriesAllowed, final Integer type, final Integer usage, final String description, final CodeValue tagId) {
this.name = StringUtils.defaultIfEmpty(name, null);
this.glCode = StringUtils.defaultIfEmpty(glCode, null);
this.disabled = BooleanUtils.toBooleanDefaultIfNull(disabled, false);
this.manualEntriesAllowed = BooleanUtils.toBooleanDefaultIfNull(manualEntriesAllowed, true);
this.usage = usage;
this.type = type;
this.description = StringUtils.defaultIfEmpty(description, null);
this.parent = parent;
this.tagId = tagId;
}
public static GLAccount fromJson(final GLAccount parent, final JsonCommand command, final CodeValue glAccountTagType) {
final String name = command.stringValueOfParameterNamed(GLAccountJsonInputParams.NAME.getValue());
final String glCode = command.stringValueOfParameterNamed(GLAccountJsonInputParams.GL_CODE.getValue());
final boolean disabled = command.booleanPrimitiveValueOfParameterNamed(GLAccountJsonInputParams.DISABLED.getValue());
final boolean manualEntriesAllowed = command.booleanPrimitiveValueOfParameterNamed(GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED
.getValue());
final Integer usage = command.integerValueSansLocaleOfParameterNamed(GLAccountJsonInputParams.USAGE.getValue());
final Integer type = command.integerValueSansLocaleOfParameterNamed(GLAccountJsonInputParams.TYPE.getValue());
final String description = command.stringValueOfParameterNamed(GLAccountJsonInputParams.DESCRIPTION.getValue());
return new GLAccount(parent, name, glCode, disabled, manualEntriesAllowed, type, usage, description, glAccountTagType);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(15);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.DESCRIPTION.getValue(), this.description);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.DISABLED.getValue(), this.disabled);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.GL_CODE.getValue(), this.glCode);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED.getValue(), this.manualEntriesAllowed);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.NAME.getValue(), this.name);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.PARENT_ID.getValue(), 0L);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.TYPE.getValue(), this.type, true);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.USAGE.getValue(), this.usage, true);
handlePropertyUpdate(command, actualChanges, GLAccountJsonInputParams.TAGID.getValue(),
this.tagId == null ? 0L : this.tagId.getId());
return actualChanges;
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final Integer propertyToBeUpdated, final boolean sansLocale) {
boolean changeDetected = false;
if (sansLocale) {
changeDetected = command.isChangeInIntegerSansLocaleParameterNamed(paramName, propertyToBeUpdated);
} else {
changeDetected = command.isChangeInIntegerParameterNamed(paramName, propertyToBeUpdated);
}
if (changeDetected) {
Integer newValue = null;
if (sansLocale) {
newValue = command.integerValueSansLocaleOfParameterNamed(paramName);
} else {
newValue = command.integerValueOfParameterNamed(paramName);
}
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(GLAccountJsonInputParams.TYPE.getValue())) {
this.type = newValue;
} else if (paramName.equals(GLAccountJsonInputParams.USAGE.getValue())) {
this.usage = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(GLAccountJsonInputParams.DESCRIPTION.getValue())) {
this.description = newValue;
} else if (paramName.equals(GLAccountJsonInputParams.GL_CODE.getValue())) {
this.glCode = newValue;
} else if (paramName.equals(GLAccountJsonInputParams.NAME.getValue())) {
this.name = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final Long propertyToBeUpdated) {
if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) {
final Long newValue = command.longValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(GLAccountJsonInputParams.PARENT_ID.getValue())) {
// do nothing as this is a nested property
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final boolean propertyToBeUpdated) {
if (command.isChangeInBooleanParameterNamed(paramName, propertyToBeUpdated)) {
final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED.getValue())) {
this.manualEntriesAllowed = newValue;
} else if (paramName.equals(GLAccountJsonInputParams.DISABLED.getValue())) {
this.disabled = newValue;
}
}
}
public boolean isHeaderAccount() {
return GLAccountUsage.HEADER.getValue().equals(this.usage);
}
public Integer getUsage() {
return this.usage;
}
public List<GLAccount> getChildren() {
return this.children;
}
public boolean isDisabled() {
return this.disabled;
}
public boolean isManualEntriesAllowed() {
return this.manualEntriesAllowed;
}
public String getGlCode() {
return this.glCode;
}
public String getName() {
return this.name;
}
public Integer getType() {
return this.type;
}
public void generateHierarchy() {
if (this.parent != null) {
this.hierarchy = this.parent.hierarchyOf(getId());
} else {
this.hierarchy = ".";
}
}
private String hierarchyOf(final Long id) {
return this.hierarchy + id.toString() + ".";
}
public boolean isDetailAccount() {
return GLAccountUsage.DETAIL.getValue().equals(this.usage);
}
public void updateTagId(final CodeValue tagID) {
this.tagId = tagID;
}
public void updateParentAccount(final GLAccount parentAccount) {
this.parent = parentAccount;
generateHierarchy();
}
}
@@ -0,0 +1,13 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface GLAccountRepository extends JpaRepository<GLAccount, Long>, JpaSpecificationExecutor<GLAccount> {
// no added behaviour
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link GLAccountRepository} .
* </p>
*/
@Service
public class GLAccountRepositoryWrapper {
private final GLAccountRepository repository;
@Autowired
public GLAccountRepositoryWrapper(final GLAccountRepository repository) {
this.repository = repository;
}
public GLAccount findOneWithNotFoundDetection(final Long id) {
final GLAccount account = this.repository.findOne(id);
if (account == null) { throw new GLAccountNotFoundException(id); }
return account;
}
}
@@ -0,0 +1,89 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import java.util.HashMap;
import java.util.Map;
public enum GLAccountType {
ASSET(1, "accountType.asset"), LIABILITY(2, "accountType.liability"), EQUITY(3, "accountType.equity"), INCOME(4, "accountType.income"), EXPENSE(
5, "accountType.expense");
private final Integer value;
private final String code;
private GLAccountType(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
private static final Map<Integer, GLAccountType> intToEnumMap = new HashMap<>();
private static int minValue;
private static int maxValue;
static {
int i = 0;
for (final GLAccountType type : GLAccountType.values()) {
if (i == 0) {
minValue = type.value;
}
intToEnumMap.put(type.value, type);
if (minValue >= type.value) {
minValue = type.value;
}
if (maxValue < type.value) {
maxValue = type.value;
}
i = i + 1;
}
}
public static GLAccountType fromInt(final int i) {
final GLAccountType type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
public static int getMinValue() {
return minValue;
}
public static int getMaxValue() {
return maxValue;
}
@Override
public String toString() {
return name().toString();
}
public boolean isAssetType() {
return this.value.equals(GLAccountType.ASSET.getValue());
}
public boolean isLiabilityType() {
return this.value.equals(GLAccountType.LIABILITY.getValue());
}
public boolean isEquityType() {
return this.value.equals(GLAccountType.EQUITY.getValue());
}
public boolean isIncomeType() {
return this.value.equals(GLAccountType.INCOME.getValue());
}
public boolean isExpenseType() {
return this.value.equals(GLAccountType.EXPENSE.getValue());
}
}
@@ -0,0 +1,69 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import java.util.HashMap;
import java.util.Map;
public enum GLAccountUsage {
DETAIL(1, "accountUsage.detail"), HEADER(2, "accountUsage.header");
private final Integer value;
private final String code;
private GLAccountUsage(final Integer value, final String code) {
this.value = value;
this.code = code;
}
public Integer getValue() {
return this.value;
}
public String getCode() {
return this.code;
}
private static final Map<Integer, GLAccountUsage> intToEnumMap = new HashMap<>();
private static int minValue;
private static int maxValue;
static {
int i = 0;
for (final GLAccountUsage type : GLAccountUsage.values()) {
if (i == 0) {
minValue = type.value;
}
intToEnumMap.put(type.value, type);
if (minValue >= type.value) {
minValue = type.value;
}
if (maxValue < type.value) {
maxValue = type.value;
}
i = i + 1;
}
}
public static GLAccountUsage fromInt(final int i) {
final GLAccountUsage type = intToEnumMap.get(Integer.valueOf(i));
return type;
}
public static int getMinValue() {
return minValue;
}
public static int getMaxValue() {
return maxValue;
}
@Override
public String toString() {
return name().toString();
}
}
@@ -0,0 +1,20 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Account with a given GL Code of
* the particular type is already present
*/
public class GLAccountDuplicateException extends AbstractPlatformDomainRuleException {
public GLAccountDuplicateException(final String glCode) {
super("error.msg.glaccount.glcode.duplicate", "General Ledger Account with GL code " + glCode + " is already present", glCode);
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when trying to fetch accounts belonging to
* an Invalid Usage Type
*/
public class GLAccountInvalidClassificationException extends AbstractPlatformDomainRuleException {
public GLAccountInvalidClassificationException(final Integer usage) {
super("error.msg.glaccount.usage.invalid", "The following COA usage is invalid: " + usage);
}
}
@@ -0,0 +1,38 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Account with a given GL Code of
* the particular type is already present
*/
public class GLAccountInvalidDeleteException extends AbstractPlatformDomainRuleException {
/*** Enum of reasons for invalid delete **/
public static enum GL_ACCOUNT_INVALID_DELETE_REASON {
TRANSANCTIONS_LOGGED, HAS_CHILDREN;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) {
return "This GL Account cannot be deleted as it has transactions logged against it";
} else if (name().toString().equalsIgnoreCase("HAS_CHILDREN")) { return "Cannot delete this Header GL Account without first deleting or reassinging its children"; }
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) {
return "error.msg.glaccount.glcode.invalid.delete.transactions.logged";
} else if (name().toString().equalsIgnoreCase("HAS_CHILDREN")) { return "error.msg.glaccount.glcode.invalid.delete.has.children"; }
return name().toString();
}
}
public GLAccountInvalidDeleteException(final GL_ACCOUNT_INVALID_DELETE_REASON reason, final Long glAccountId) {
super(reason.errorCode(), reason.errorMessage(), glAccountId);
}
}
@@ -0,0 +1,20 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when trying to map invalid parents to a GL
* account
*/
public class GLAccountInvalidParentException extends AbstractPlatformDomainRuleException {
public GLAccountInvalidParentException(final long glAccountId) {
super("error.msg.glaccount.parent.invalid", "The account with id " + glAccountId
+ " is a 'Detail' account and cannot be used as a parent", glAccountId);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when an error is encountered during
* updating a GL Account
*/
public class GLAccountInvalidUpdateException extends AbstractPlatformDomainRuleException {
/*** Enum of reasons for invalid delete **/
public static enum GL_ACCOUNT_INVALID_UPDATE_REASON {
TRANSANCTIONS_LOGGED;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) { return "This Usage of this (detail) GL Account as it already has transactions logged against it"; }
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("TRANSANCTIONS_LOGGED")) { return "error.msg.glaccount.glcode.invalid.update.transactions.logged"; }
return name().toString();
}
}
public GLAccountInvalidUpdateException(final GL_ACCOUNT_INVALID_UPDATE_REASON reason, final Long glAccountId) {
super(reason.errorCode(), reason.errorMessage(), glAccountId);
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when trying to fetch accounts belonging to
* an Invalid Type
*/
public class GLAccountInvalidUsageException extends AbstractPlatformDomainRuleException {
public GLAccountInvalidUsageException(final Integer type) {
super("error.msg.glaccount.classification.invalid", "The following COA type is invalid: " + type);
}
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when GL account resources are not found.
*/
public class GLAccountNotFoundException extends AbstractPlatformResourceNotFoundException {
public GLAccountNotFoundException(final Long id) {
super("error.msg.glaccount.id.invalid", "General Ledger account with identifier " + id + " does not exist ", id);
}
}
@@ -0,0 +1,17 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class InvalidParentGLAccountHeadException extends AbstractPlatformDomainRuleException {
public InvalidParentGLAccountHeadException(final Long glAccountId, final Long parentId) {
super("error.msg.glaccount.id.and.parentid.must.not.same", "parentId:" + parentId + ", id" + glAccountId + " should not be same",
glAccountId, parentId);
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLACCOUNT", action = "CREATE")
public class CreateGLAccountCommandHandler implements NewCommandSourceHandler {
private final GLAccountWritePlatformService writePlatformService;
@Autowired
public CreateGLAccountCommandHandler(final GLAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createGLAccount(command);
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLACCOUNT", action = "DELETE")
public class DeleteGLAccountCommandHandler implements NewCommandSourceHandler {
private final GLAccountWritePlatformService writePlatformService;
@Autowired
public DeleteGLAccountCommandHandler(final GLAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.deleteGLAccount(command.entityId());
}
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
import org.mifosplatform.commands.annotation.CommandType;
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "GLACCOUNT", action = "UPDATE")
public class UpdateGLAccountCommandHandler implements NewCommandSourceHandler {
private final GLAccountWritePlatformService writePlatformService;
@Autowired
public UpdateGLAccountCommandHandler(final GLAccountWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.updateGLAccount(command.entityId(), command);
}
}
@@ -0,0 +1,64 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.serialization;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.glaccount.api.GLAccountJsonInputParams;
import org.mifosplatform.accounting.glaccount.command.GLAccountCommand;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for
* {@link GuarantorCommand}'s.
*/
@Component
public final class GLAccountCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<GLAccountCommand> {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public GLAccountCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
}
@Override
public GLAccountCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = GLAccountJsonInputParams.getAllValues();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long id = this.fromApiJsonHelper.extractLongNamed(GLAccountJsonInputParams.ID.getValue(), element);
final String name = this.fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.NAME.getValue(), element);
final Long parentId = this.fromApiJsonHelper.extractLongNamed(GLAccountJsonInputParams.PARENT_ID.getValue(), element);
final String glCode = this.fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.GL_CODE.getValue(), element);
final Boolean disabled = this.fromApiJsonHelper.extractBooleanNamed(GLAccountJsonInputParams.DISABLED.getValue(), element);
final Boolean manualEntriesAllowed = this.fromApiJsonHelper.extractBooleanNamed(
GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED.getValue(), element);
final Integer type = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(GLAccountJsonInputParams.TYPE.getValue(), element);
final Integer usage = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(GLAccountJsonInputParams.USAGE.getValue(), element);
final String description = this.fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.DESCRIPTION.getValue(), element);
final Long tagId = this.fromApiJsonHelper.extractLongNamed(GLAccountJsonInputParams.TAGID.getValue(), element);
return new GLAccountCommand(id, name, parentId, glCode, disabled, manualEntriesAllowed, type, usage, description, tagId);
}
}
@@ -0,0 +1,31 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.service;
import java.util.List;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.journalentry.data.JournalEntryAssociationParametersData;
public interface GLAccountReadPlatformService {
List<GLAccountData> retrieveAllGLAccounts(Integer accountClassification, String searchParam, Integer usage,
Boolean manualTransactionsAllowed, Boolean disabled, JournalEntryAssociationParametersData associationParametersData);
GLAccountData retrieveGLAccountById(long glAccountId, JournalEntryAssociationParametersData associationParametersData);
List<GLAccountData> retrieveAllEnabledDetailGLAccounts();
List<GLAccountData> retrieveAllEnabledDetailGLAccounts(GLAccountType accountType);
List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(GLAccountType accountType);
GLAccountData retrieveNewGLAccountDetails(final Integer type);
List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType);
}
@@ -0,0 +1,268 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.common.AccountingEnumerations;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidClassificationException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.mifosplatform.accounting.journalentry.data.JournalEntryAssociationParametersData;
import org.mifosplatform.infrastructure.codes.data.CodeValueData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
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 GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final static String nameDecoratedBaseOnHierarchy = "concat(substring('........................................', 1, ((LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '.', '')) - 1) * 4)), name)";
@Autowired
public GLAccountReadPlatformServiceImpl(final RoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class GLAccountMapper implements RowMapper<GLAccountData> {
private final JournalEntryAssociationParametersData associationParametersData;
public GLAccountMapper(final JournalEntryAssociationParametersData associationParametersData) {
if (associationParametersData == null) {
this.associationParametersData = new JournalEntryAssociationParametersData();
} else {
this.associationParametersData = associationParametersData;
}
}
public String schema() {
StringBuilder sb = new StringBuilder();
sb.append(
" gl.id as id, name as name, parent_id as parentId, gl_code as glCode, disabled as disabled, manual_journal_entries_allowed as manualEntriesAllowed, ")
.append("classification_enum as classification, account_usage as accountUsage, gl.description as description, ")
.append(nameDecoratedBaseOnHierarchy).append(" as nameDecorated, ")
.append("cv.id as codeId, cv.code_value as codeValue ");
if (this.associationParametersData.isRunningBalanceRequired()) {
sb.append(",gl_j.organization_running_balance as organizationRunningBalance ");
}
sb.append("from acc_gl_account gl left join m_code_value cv on tag_id=cv.id ");
if (this.associationParametersData.isRunningBalanceRequired()) {
sb.append("left outer Join acc_gl_journal_entry gl_j on gl_j.account_id = gl.id");
}
return sb.toString();
}
@Override
public GLAccountData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final String name = rs.getString("name");
final Long parentId = JdbcSupport.getLong(rs, "parentId");
final String glCode = rs.getString("glCode");
final boolean disabled = rs.getBoolean("disabled");
final boolean manualEntriesAllowed = rs.getBoolean("manualEntriesAllowed");
final int accountTypeId = JdbcSupport.getInteger(rs, "classification");
final EnumOptionData accountType = AccountingEnumerations.gLAccountType(accountTypeId);
final int usageId = JdbcSupport.getInteger(rs, "accountUsage");
final EnumOptionData usage = AccountingEnumerations.gLAccountUsage(usageId);
final String description = rs.getString("description");
final String nameDecorated = rs.getString("nameDecorated");
final Long codeId = rs.wasNull() ? null : rs.getLong("codeId");
final String codeValue = rs.getString("codeValue");
final CodeValueData tagId = CodeValueData.instance(codeId, codeValue);
Long organizationRunningBalance = null;
if (associationParametersData.isRunningBalanceRequired()) {
organizationRunningBalance = rs.getLong("organizationRunningBalance");
}
return new GLAccountData(id, name, parentId, glCode, disabled, manualEntriesAllowed, accountType, usage, description,
nameDecorated, tagId, organizationRunningBalance);
}
}
@Override
public List<GLAccountData> retrieveAllGLAccounts(final Integer accountClassification, final String searchParam, final Integer usage,
final Boolean manualTransactionsAllowed, final Boolean disabled, JournalEntryAssociationParametersData associationParametersData) {
if (accountClassification != null) {
if (!checkValidGLAccountType(accountClassification)) { throw new GLAccountInvalidClassificationException(accountClassification); }
}
if (usage != null) {
if (!checkValidGLAccountUsage(usage)) { throw new GLAccountInvalidClassificationException(accountClassification); }
}
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
String sql = "select " + rm.schema();
// append SQL statement for fetching account totals
if (associationParametersData.isRunningBalanceRequired()) {
sql = sql + " and gl_j.id in (select t1.id from (select t2.account_id, max(t2.id) as id from "
+ "(select id, max(entry_date) as entry_date, account_id from acc_gl_journal_entry where is_running_balance_calculated = 1 "
+ "group by account_id desc) t3 inner join acc_gl_journal_entry t2 on t2.account_id = t3.account_id and t2.entry_date = t3.entry_date "
+ "group by t2.account_id desc) t1)";
}
final Object[] paramaterArray = new Object[3];
int arrayPos = 0;
boolean filtersPresent = false;
if ((accountClassification != null) || StringUtils.isNotBlank(searchParam) || (usage != null)
|| (manualTransactionsAllowed != null) || (disabled != null)) {
filtersPresent = true;
sql += " where";
}
if (filtersPresent) {
boolean firstWhereConditionAdded = false;
if (accountClassification != null) {
sql += " classification_enum like ?";
paramaterArray[arrayPos] = accountClassification;
arrayPos = arrayPos + 1;
firstWhereConditionAdded = true;
}
if (StringUtils.isNotBlank(searchParam)) {
if (firstWhereConditionAdded) {
sql += " and ";
}
sql += " ( name like %?% or gl_code like %?% )";
paramaterArray[arrayPos] = searchParam;
arrayPos = arrayPos + 1;
paramaterArray[arrayPos] = searchParam;
arrayPos = arrayPos + 1;
firstWhereConditionAdded = true;
}
if (usage != null) {
if (firstWhereConditionAdded) {
sql += " and ";
}
if (GLAccountUsage.HEADER.getValue().equals(usage)) {
sql += " account_usage = 2 ";
} else if (GLAccountUsage.DETAIL.getValue().equals(usage)) {
sql += " account_usage = 1 ";
}
firstWhereConditionAdded = true;
}
if (manualTransactionsAllowed != null) {
if (firstWhereConditionAdded) {
sql += " and ";
}
if (manualTransactionsAllowed) {
sql += " manual_journal_entries_allowed = 1";
} else {
sql += " manual_journal_entries_allowed = 0";
}
firstWhereConditionAdded = true;
}
if (disabled != null) {
if (firstWhereConditionAdded) {
sql += " and ";
}
if (disabled) {
sql += " disabled = 1";
} else {
sql += " disabled = 0";
}
firstWhereConditionAdded = true;
}
}
final Object[] finalObjectArray = Arrays.copyOf(paramaterArray, arrayPos);
return this.jdbcTemplate.query(sql, rm, finalObjectArray);
}
@Override
public GLAccountData retrieveGLAccountById(final long glAccountId, JournalEntryAssociationParametersData associationParametersData) {
try {
final GLAccountMapper rm = new GLAccountMapper(associationParametersData);
final StringBuilder sql = new StringBuilder();
sql.append("select ").append(rm.schema());
if (associationParametersData.isRunningBalanceRequired()) {
sql.append(" and gl_j.is_running_balance_calculated = 1 ");
}
sql.append("where gl.id = ?");
if (associationParametersData.isRunningBalanceRequired()) {
sql.append(" ORDER BY gl_j.entry_date DESC,gl_j.id DESC LIMIT 1");
}
final GLAccountData glAccountData = this.jdbcTemplate.queryForObject(sql.toString(), rm, new Object[] { glAccountId });
return glAccountData;
} catch (final EmptyResultDataAccessException e) {
throw new GLAccountNotFoundException(glAccountId);
}
}
@Override
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts(final GLAccountType accountType) {
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false,
new JournalEntryAssociationParametersData());
}
@Override
public List<GLAccountData> retrieveAllEnabledDetailGLAccounts() {
return retrieveAllGLAccounts(null, null, GLAccountUsage.DETAIL.getValue(), null, false, new JournalEntryAssociationParametersData());
}
private static boolean checkValidGLAccountType(final int type) {
for (final GLAccountType accountType : GLAccountType.values()) {
if (accountType.getValue().equals(type)) { return true; }
}
return false;
}
private static boolean checkValidGLAccountUsage(final int type) {
for (final GLAccountUsage accountUsage : GLAccountUsage.values()) {
if (accountUsage.getValue().equals(type)) { return true; }
}
return false;
}
@Override
public GLAccountData retrieveNewGLAccountDetails(final Integer type) {
return GLAccountData.sensibleDefaultsForNewGLAccountCreation(type);
}
@Override
public List<GLAccountData> retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) {
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false,
new JournalEntryAssociationParametersData());
}
@Override
public List<GLAccountDataForLookup> retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) {
final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper();
final String sql = "Select " + mapper.schema() + " where rule.id=? and tags.acc_type_enum=?";
return this.jdbcTemplate.query(sql, mapper, new Object[] { ruleId, transactionType });
}
private static final class GLAccountDataLookUpMapper implements RowMapper<GLAccountDataForLookup> {
public String schema() {
return " gl.id as id, gl.name as name, gl.gl_code as glCode from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id";
}
@Override
public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = JdbcSupport.getLong(rs, "id");
final String name = rs.getString("name");
final String glCode = rs.getString("glCode");
return new GLAccountDataForLookup(id, name, glCode);
}
}
}
@@ -0,0 +1,19 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.service;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface GLAccountWritePlatformService {
CommandProcessingResult createGLAccount(JsonCommand command);
CommandProcessingResult updateGLAccount(Long glAccountId, JsonCommand command);
CommandProcessingResult deleteGLAccount(Long glAccountId);
}
@@ -0,0 +1,227 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.service;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.common.AccountingConstants;
import org.mifosplatform.accounting.glaccount.api.GLAccountJsonInputParams;
import org.mifosplatform.accounting.glaccount.command.GLAccountCommand;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.exception.GLAccountDuplicateException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidDeleteException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidDeleteException.GL_ACCOUNT_INVALID_DELETE_REASON;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidParentException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidUpdateException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidUpdateException.GL_ACCOUNT_INVALID_UPDATE_REASON;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.mifosplatform.accounting.glaccount.exception.InvalidParentGLAccountHeadException;
import org.mifosplatform.accounting.glaccount.serialization.GLAccountCommandFromApiJsonDeserializer;
import org.mifosplatform.accounting.journalentry.domain.JournalEntry;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryRepository;
import org.mifosplatform.infrastructure.codes.domain.CodeValue;
import org.mifosplatform.infrastructure.codes.domain.CodeValueRepositoryWrapper;
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.core.exception.PlatformDataIntegrityException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 GLAccountWritePlatformServiceJpaRepositoryImpl implements GLAccountWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GLAccountWritePlatformServiceJpaRepositoryImpl.class);
private final GLAccountRepository glAccountRepository;
private final JournalEntryRepository glJournalEntryRepository;
private final GLAccountCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final CodeValueRepositoryWrapper codeValueRepositoryWrapper;
@Autowired
public GLAccountWritePlatformServiceJpaRepositoryImpl(final GLAccountRepository glAccountRepository,
final JournalEntryRepository glJournalEntryRepository, final GLAccountCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final CodeValueRepositoryWrapper codeValueRepositoryWrapper) {
this.glAccountRepository = glAccountRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
}
@Transactional
@Override
public CommandProcessingResult createGLAccount(final JsonCommand command) {
try {
final GLAccountCommand accountCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
accountCommand.validateForCreate();
// check parent is valid
final Long parentId = command.longValueOfParameterNamed(GLAccountJsonInputParams.PARENT_ID.getValue());
GLAccount parentGLAccount = null;
if (parentId != null) {
parentGLAccount = validateParentGLAccount(parentId);
}
CodeValue glAccountTagType = null;
final Long tagId = command.longValueOfParameterNamed(GLAccountJsonInputParams.TAGID.getValue());
final Long type = command.longValueOfParameterNamed(GLAccountJsonInputParams.TYPE.getValue());
final GLAccountType accountType = GLAccountType.fromInt(type.intValue());
if (tagId != null) {
glAccountTagType = retrieveTagId(tagId, accountType);
}
final GLAccount glAccount = GLAccount.fromJson(parentGLAccount, command, glAccountTagType);
this.glAccountRepository.saveAndFlush(glAccount);
glAccount.generateHierarchy();
this.glAccountRepository.save(glAccount);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(glAccount.getId()).build();
} catch (final DataIntegrityViolationException dve) {
handleGLAccountDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult updateGLAccount(final Long glAccountId, final JsonCommand command) {
try {
final GLAccountCommand accountCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
accountCommand.validateForUpdate();
final Long parentId = command.longValueOfParameterNamed(GLAccountJsonInputParams.PARENT_ID.getValue());
if (glAccountId.equals(parentId)) { throw new InvalidParentGLAccountHeadException(glAccountId, parentId); }
// is the glAccount valid
final GLAccount glAccount = this.glAccountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
final Map<String, Object> changesOnly = glAccount.update(command);
// is the new parent valid
if (changesOnly.containsKey(GLAccountJsonInputParams.PARENT_ID.getValue())) {
final GLAccount parentAccount = validateParentGLAccount(parentId);
glAccount.updateParentAccount(parentAccount);
}
if (changesOnly.containsKey(GLAccountJsonInputParams.TAGID.getValue())) {
final Long tagIdLongValue = command.longValueOfParameterNamed(GLAccountJsonInputParams.TAGID.getValue());
final GLAccountType accountType = GLAccountType.fromInt(glAccount.getType());
CodeValue tagID = null;
if (tagIdLongValue != null) {
tagID = retrieveTagId(tagIdLongValue, accountType);
}
glAccount.updateTagId(tagID);
}
/**
* a detail account cannot be changed to a header account if
* transactions are already logged against it
**/
if (changesOnly.containsKey(GLAccountJsonInputParams.USAGE.getValue())) {
if (glAccount.isHeaderAccount()) {
final List<JournalEntry> journalEntriesForAccount = this.glJournalEntryRepository
.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) { throw new GLAccountInvalidUpdateException(
GL_ACCOUNT_INVALID_UPDATE_REASON.TRANSANCTIONS_LOGGED, glAccountId); }
}
}
if (!changesOnly.isEmpty()) {
this.glAccountRepository.saveAndFlush(glAccount);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(glAccount.getId())
.with(changesOnly).build();
} catch (final DataIntegrityViolationException dve) {
handleGLAccountDataIntegrityIssues(command, dve);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public CommandProcessingResult deleteGLAccount(final Long glAccountId) {
final GLAccount glAccount = this.glAccountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
// validate this isn't a header account that has children
if (glAccount.isHeaderAccount() && glAccount.getChildren().size() > 0) { throw new GLAccountInvalidDeleteException(
GL_ACCOUNT_INVALID_DELETE_REASON.HAS_CHILDREN, glAccountId); }
// does this account have transactions logged against it
final List<JournalEntry> journalEntriesForAccount = this.glJournalEntryRepository.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) { throw new GLAccountInvalidDeleteException(
GL_ACCOUNT_INVALID_DELETE_REASON.TRANSANCTIONS_LOGGED, glAccountId); }
this.glAccountRepository.delete(glAccount);
return new CommandProcessingResultBuilder().withEntityId(glAccountId).build();
}
/**
* @param command
* @return
*/
private GLAccount validateParentGLAccount(final Long parentAccountId) {
GLAccount parentGLAccount = null;
if (parentAccountId != null) {
parentGLAccount = this.glAccountRepository.findOne(parentAccountId);
if (parentGLAccount == null) { throw new GLAccountNotFoundException(parentAccountId); }
// ensure parent is not a detail account
if (parentGLAccount.isDetailAccount()) { throw new GLAccountInvalidParentException(parentAccountId); }
}
return parentGLAccount;
}
/**
* @param command
* @param dve
*/
private void handleGLAccountDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) {
final Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("acc_gl_code")) {
final String glCode = command.stringValueOfParameterNamed(GLAccountJsonInputParams.GL_CODE.getValue());
throw new GLAccountDuplicateException(glCode);
}
logger.error(dve.getMessage(), dve);
throw new PlatformDataIntegrityException("error.msg.glAccount.unknown.data.integrity.issue",
"Unknown data integrity issue with resource GL Account: " + realCause.getMessage());
}
private CodeValue retrieveTagId(final Long tagId, final GLAccountType accountType) {
CodeValue glAccountTagType = null;
if (accountType.isAssetType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isLiabilityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isEquityType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isIncomeType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.INCOME_TAG_OPTION_CODE_NAME, tagId);
} else if (accountType.isExpenseType()) {
glAccountTagType = this.codeValueRepositoryWrapper.findOneByCodeNameAndIdWithNotFoundDetection(
AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME, tagId);
}
return glAccountTagType;
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.api;
import java.util.Date;
import java.util.Locale;
import javax.ws.rs.WebApplicationException;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
/**
* Class for parsing dates sent as query parameters
*
* TODO: Vishwas Should move this class to a more generic package
*/
public class DateParam {
private final String dateAsString;
public DateParam(final String dateStr) throws WebApplicationException {
this.dateAsString = dateStr;
}
public Date getDate(final String parameterName, final String dateFormat, final String localeAsString) {
final Locale locale = JsonParserHelper.localeFromString(localeAsString);
final LocalDate localDate = JsonParserHelper.convertFrom(this.dateAsString, parameterName, dateFormat, locale);
return localDate.toDate();
}
}
@@ -0,0 +1,199 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.api;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.journalentry.data.JournalEntryAssociationParametersData;
import org.mifosplatform.accounting.journalentry.data.JournalEntryData;
import org.mifosplatform.accounting.journalentry.data.OfficeOpeningBalancesData;
import org.mifosplatform.accounting.journalentry.service.JournalEntryReadPlatformService;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
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.exception.UnrecognizedQueryParamException;
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.mifosplatform.infrastructure.core.service.Page;
import org.mifosplatform.infrastructure.core.service.SearchParameters;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Path("/journalentries")
@Component
@Scope("singleton")
public class JournalEntriesApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "officeId", "officeName",
"glAccountName", "glAccountId", "glAccountCode", "glAccountType", "transactionDate", "entryType", "amount", "transactionId",
"manualEntry", "entityType", "entityId", "createdByUserId", "createdDate", "createdByUserName", "comments", "reversed",
"referenceNumber", "currency", "transactionDetails"));
private final String resourceNameForPermission = "JOURNALENTRY";
private final JournalEntryReadPlatformService journalEntryReadPlatformService;
private final DefaultToApiJsonSerializer<Object> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public JournalEntriesApiResource(final PlatformSecurityContext context,
final JournalEntryReadPlatformService journalEntryReadPlatformService,
final DefaultToApiJsonSerializer<Object> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.journalEntryReadPlatformService = journalEntryReadPlatformService;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAll(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("glAccountId") final Long glAccountId, @QueryParam("manualEntriesOnly") final Boolean onlyManualEntries,
@QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam,
@QueryParam("transactionId") final String transactionId, @QueryParam("entityType") final Integer entityType,
@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("orderBy") final String orderBy, @QueryParam("sortOrder") final String sortOrder,
@QueryParam("locale") final String locale, @QueryParam("dateFormat") final String dateFormat,
@QueryParam("loanId") final Long loanId, @QueryParam("savingsId") final Long savingsId,
@QueryParam("runningBalance") final boolean runningBalance, @QueryParam("transactionDetails") final boolean transactionDetails) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
Date fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate("fromDate", dateFormat, locale);
}
Date toDate = null;
if (toDateParam != null) {
toDate = toDateParam.getDate("toDate", dateFormat, locale);
}
final SearchParameters searchParameters = SearchParameters.forJournalEntries(officeId, offset, limit, orderBy, sortOrder, loanId,
savingsId);
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(transactionDetails,
runningBalance);
final Page<JournalEntryData> glJournalEntries = this.journalEntryReadPlatformService.retrieveAll(searchParameters, glAccountId,
onlyManualEntries, fromDate, toDate, transactionId, entityType, associationParametersData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glJournalEntries, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{journalEntryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveJournalEntryById(@PathParam("journalEntryId") final Long journalEntryId, @Context final UriInfo uriInfo,
@QueryParam("runningBalance") final boolean runningBalance, @QueryParam("transactionDetails") final boolean transactionDetails) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
JournalEntryAssociationParametersData associationParametersData = new JournalEntryAssociationParametersData(transactionDetails,
runningBalance);
final JournalEntryData glJournalEntryData = this.journalEntryReadPlatformService.retrieveGLJournalEntryById(journalEntryId,
associationParametersData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glJournalEntryData, RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLJournalEntry(final String jsonRequestBody, @QueryParam("command") final String commandParam) {
CommandProcessingResult result = null;
if (is(commandParam, "updateRunningBalance")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateRunningBalanceForJournalEntry()
.withJson(jsonRequestBody).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else if (is(commandParam, "defineOpeningBalance")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().defineOpeningBalanceForJournalEntry()
.withJson(jsonRequestBody).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createJournalEntry().withJson(jsonRequestBody).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
}
return this.apiJsonSerializerService.serialize(result);
}
@POST
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createReversalJournalEntry(final String jsonRequestBody, @PathParam("transactionId") final String transactionId,
@QueryParam("command") final String commandParam) {
CommandProcessingResult result = null;
if (is(commandParam, "reverse")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().reverseJournalEntry(transactionId).withJson(jsonRequestBody)
.build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.apiJsonSerializerService.serialize(result);
}
@GET
@Path("provisioning")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveJournalEntries(@QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit,
@QueryParam("entryId") final Long entryId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser();
String transactionId = "P"+entryId ;
SearchParameters params = SearchParameters.forPagination(offset, limit) ;
Page<JournalEntryData> entries = this.journalEntryReadPlatformService.retrieveAll(params, null, null, null, null, transactionId, PortfolioProductType.PROVISIONING.getValue(), null) ;
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, entries, RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Path("openingbalance")
public String retrieveOpeningBalance(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("currencyCode") final String currencyCode) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final OfficeOpeningBalancesData officeOpeningBalancesData = this.journalEntryReadPlatformService.retrieveOfficeOpeningBalances(
officeId, currencyCode);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, officeOpeningBalancesData);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -0,0 +1,45 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a journal Entry
***/
public enum JournalEntryJsonInputParams {
OFFICE_ID("officeId"), TRANSACTION_DATE("transactionDate"), COMMENTS("comments"), CREDITS("credits"), DEBITS("debits"), LOCALE("locale"), DATE_FORMAT(
"dateFormat"), REFERENCE_NUMBER("referenceNumber"), USE_ACCOUNTING_RULE("useAccountingRule"), ACCOUNTING_RULE("accountingRule"), AMOUNT(
"amount"), CURRENCY_CODE("currencyCode"), PAYMENT_TYPE_ID("paymentTypeId"), ACCOUNT_NUMBER("accountNumber"), CHECK_NUMBER(
"checkNumber"), ROUTING_CODE("routingCode"), RECEIPT_NUMBER("receiptNumber"), BANK_NUMBER("bankNumber");
private final String value;
private JournalEntryJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final JournalEntryJsonInputParams type : JournalEntryJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,159 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.command;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
/**
* Immutable command for adding an accounting closure
*/
public class JournalEntryCommand {
private final Long officeId;
private final LocalDate transactionDate;
private final String currencyCode;
private final String comments;
private final String referenceNumber;
private final Long accountingRuleId;
private final BigDecimal amount;
private final Long paymentTypeId;
@SuppressWarnings("unused")
private final String accountNumber;
@SuppressWarnings("unused")
private final String checkNumber;
@SuppressWarnings("unused")
private final String receiptNumber;
@SuppressWarnings("unused")
private final String bankNumber;
@SuppressWarnings("unused")
private final String routingCode;
private final SingleDebitOrCreditEntryCommand[] credits;
private final SingleDebitOrCreditEntryCommand[] debits;
public JournalEntryCommand(final Long officeId, final String currencyCode, final LocalDate transactionDate, final String comments,
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits, final String referenceNumber,
final Long accountingRuleId, final BigDecimal amount, final Long paymentTypeId, final String accountNumber,
final String checkNumber, final String receiptNumber, final String bankNumber, final String routingCode) {
this.officeId = officeId;
this.currencyCode = currencyCode;
this.transactionDate = transactionDate;
this.comments = comments;
this.credits = credits;
this.debits = debits;
this.referenceNumber = referenceNumber;
this.accountingRuleId = accountingRuleId;
this.amount = amount;
this.paymentTypeId = paymentTypeId;
this.accountNumber = accountNumber;
this.checkNumber = checkNumber;
this.receiptNumber = receiptNumber;
this.bankNumber = bankNumber;
this.routingCode = routingCode;
}
public void validateForCreate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLJournalEntry");
baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notBlank();
baseDataValidator.reset().parameter("officeId").value(this.officeId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(JournalEntryJsonInputParams.CURRENCY_CODE.getValue()).value(this.currencyCode).notBlank();
baseDataValidator.reset().parameter("comments").value(this.comments).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("referenceNumber").value(this.referenceNumber).ignoreIfNull().notExceedingLengthOf(100);
baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).ignoreIfNull().longGreaterThanZero();
baseDataValidator.reset().parameter("paymentTypeId").value(this.paymentTypeId).ignoreIfNull().longGreaterThanZero();
// validation for credit array elements
if (this.credits != null) {
if (this.credits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
i++;
}
}
}
// validation for debit array elements
if (this.debits != null) {
if (this.debits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "debits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
i++;
}
}
}
baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().zeroOrPositiveAmount();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
/**
* @param baseDataValidator
* @param i
* @param credit
*/
private void validateSingleDebitOrCredit(final DataValidatorBuilder baseDataValidator, final String paramSuffix, final int arrayPos,
final SingleDebitOrCreditEntryCommand credit) {
baseDataValidator.reset().parameter(paramSuffix + "[" + arrayPos + "].glAccountId").value(credit.getGlAccountId()).notNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(paramSuffix + "[" + arrayPos + "].amount").value(credit.getAmount()).notNull()
.zeroOrPositiveAmount();
}
public Long getOfficeId() {
return this.officeId;
}
public LocalDate getTransactionDate() {
return this.transactionDate;
}
public String getComments() {
return this.comments;
}
public SingleDebitOrCreditEntryCommand[] getCredits() {
return this.credits;
}
public SingleDebitOrCreditEntryCommand[] getDebits() {
return this.debits;
}
public String getReferenceNumber() {
return this.referenceNumber;
}
public Long getAccountingRuleId() {
return this.accountingRuleId;
}
}
@@ -0,0 +1,58 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.command;
import java.math.BigDecimal;
import java.util.Set;
/**
* Immutable command for adding a "Credit" entry to the Journal
*/
public class SingleDebitOrCreditEntryCommand {
private final Long glAccountId;
private final BigDecimal amount;
private final String comments;
private final Set<String> parametersPassedInRequest;
public SingleDebitOrCreditEntryCommand(final Set<String> parametersPassedInRequest, final Long glAccountId, final BigDecimal amount,
final String comments) {
this.parametersPassedInRequest = parametersPassedInRequest;
this.glAccountId = glAccountId;
this.amount = amount;
this.comments = comments;
}
public boolean isGlAccountIdChanged() {
return this.parametersPassedInRequest.contains("glAccountId");
}
public boolean isGlAmountChanged() {
return this.parametersPassedInRequest.contains("amount");
}
public boolean isCommentsChanged() {
return this.parametersPassedInRequest.contains("comments");
}
public Long getGlAccountId() {
return this.glAccountId;
}
public BigDecimal getAmount() {
return this.amount;
}
public String getComments() {
return this.comments;
}
public Set<String> getParametersPassedInRequest() {
return this.parametersPassedInRequest;
}
}
@@ -0,0 +1,34 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
public class ChargePaymentDTO {
private final Long chargeId;
private final BigDecimal amount;
private final Long loanChargeId;
public ChargePaymentDTO(final Long chargeId, final Long loanChargeId, final BigDecimal amount) {
this.chargeId = chargeId;
this.amount = amount;
this.loanChargeId = loanChargeId;
}
public Long getChargeId() {
return this.chargeId;
}
public BigDecimal getAmount() {
return this.amount;
}
public Long getLoanChargeId() {
return this.loanChargeId;
}
}
@@ -0,0 +1,47 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
public class ClientChargePaymentDTO {
private final Long chargeId;
private final BigDecimal amount;
private final Long clientChargeId;
private final boolean isPenalty;
private final Long incomeAccountId;
public ClientChargePaymentDTO(Long chargeId, BigDecimal amount, Long clientChargeId, boolean isPenalty, Long incomeAccountId) {
super();
this.chargeId = chargeId;
this.amount = amount;
this.clientChargeId = clientChargeId;
this.isPenalty = isPenalty;
this.incomeAccountId = incomeAccountId;
}
public BigDecimal getAmount() {
return this.amount;
}
public Long getClientChargeId() {
return this.clientChargeId;
}
public boolean isPenalty() {
return this.isPenalty;
}
public Long getChargeId() {
return chargeId;
}
public Long getIncomeAccountId() {
return this.incomeAccountId;
}
}
@@ -0,0 +1,100 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.portfolio.client.domain.ClientTransactionType;
public class ClientTransactionDTO {
private final Long officeId;
private final Long clientId;
private String currencyCode;
private final Long transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final EnumOptionData transactionType;
private final BigDecimal amount;
private final Boolean accountingEnabled;
/*** Boolean values determines if the transaction is reversed ***/
private final boolean reversed;
/** Breakdowns of fees this Transaction pays **/
private final List<ClientChargePaymentDTO> chargePayments;
public ClientTransactionDTO(final Long clientId, final Long officeId, final Long paymentTypeId, final Long transactionId,
final Date transactionDate, final EnumOptionData transactionType, final String currencyCode, final BigDecimal amount,
final boolean reversed, final boolean accountingEnabled, final List<ClientChargePaymentDTO> clientChargePayments) {
this.clientId = clientId;
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.reversed = reversed;
this.transactionType = transactionType;
this.chargePayments = clientChargePayments;
this.officeId = officeId;
this.accountingEnabled = accountingEnabled;
this.currencyCode = currencyCode;
}
public Long getOfficeId() {
return this.officeId;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public BigDecimal getAmount() {
return this.amount;
}
public boolean isReversed() {
return this.reversed;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
public EnumOptionData getTransactionType() {
return transactionType;
}
public boolean isChargePayment() {
return ClientTransactionType.PAY_CHARGE.getValue().equals(new Integer(this.transactionType.getId().intValue()));
}
public String getCurrencyCode() {
return this.currencyCode;
}
public List<ClientChargePaymentDTO> getChargePayments() {
return this.chargePayments;
}
public Long getTransactionId() {
return this.transactionId;
}
public Boolean getAccountingEnabled() {
return this.accountingEnabled;
}
public Long getClientId() {
return this.clientId;
}
}
@@ -0,0 +1,30 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
public class JournalEntryAssociationParametersData {
private final boolean transactionDetailsRequired;
private final boolean runningBalanceRequired;
public JournalEntryAssociationParametersData() {
this.transactionDetailsRequired = false;
this.runningBalanceRequired = false;
}
public JournalEntryAssociationParametersData(final boolean transactionDetailsRequired, final boolean runningBalanceRequired) {
this.transactionDetailsRequired = transactionDetailsRequired;
this.runningBalanceRequired = runningBalanceRequired;
}
public boolean isTransactionDetailsRequired() {
return this.transactionDetailsRequired;
}
public boolean isRunningBalanceRequired() {
return this.runningBalanceRequired;
}
}
@@ -0,0 +1,162 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.organisation.monetary.data.CurrencyData;
/**
* Immutable object representing a General Ledger Account
*
* Note: no getter/setters required as google will produce json from fields of
* object.
*/
public class JournalEntryData {
private final Long id;
private final Long officeId;
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final String glAccountName;
private final Long glAccountId;
@SuppressWarnings("unused")
private final String glAccountCode;
private final EnumOptionData glAccountType;
@SuppressWarnings("unused")
private final LocalDate transactionDate;
private final EnumOptionData entryType;
private final BigDecimal amount;
@SuppressWarnings("unused")
private final CurrencyData currency;
private final String transactionId;
@SuppressWarnings("unused")
private final Boolean manualEntry;
@SuppressWarnings("unused")
private final EnumOptionData entityType;
@SuppressWarnings("unused")
private final Long entityId;
@SuppressWarnings("unused")
private final Long createdByUserId;
@SuppressWarnings("unused")
private final LocalDate createdDate;
@SuppressWarnings("unused")
private final String createdByUserName;
@SuppressWarnings("unused")
private final String comments;
@SuppressWarnings("unused")
private final Boolean reversed;
@SuppressWarnings("unused")
private final String referenceNumber;
@SuppressWarnings("unused")
private final BigDecimal officeRunningBalance;
@SuppressWarnings("unused")
private final BigDecimal organizationRunningBalance;
@SuppressWarnings("unused")
private final Boolean runningBalanceComputed;
@SuppressWarnings("unused")
private final TransactionDetailData transactionDetails;
public JournalEntryData(final Long id, final Long officeId, final String officeName, final String glAccountName,
final Long glAccountId, final String glAccountCode, final EnumOptionData glAccountClassification,
final LocalDate transactionDate, final EnumOptionData entryType, final BigDecimal amount, final String transactionId,
final Boolean manualEntry, final EnumOptionData entityType, final Long entityId, final Long createdByUserId,
final LocalDate createdDate, final String createdByUserName, final String comments, final Boolean reversed,
final String referenceNumber, final BigDecimal officeRunningBalance, final BigDecimal organizationRunningBalance,
final Boolean runningBalanceComputed, final TransactionDetailData transactionDetailData, final CurrencyData currency) {
this.id = id;
this.officeId = officeId;
this.officeName = officeName;
this.glAccountName = glAccountName;
this.glAccountId = glAccountId;
this.glAccountCode = glAccountCode;
this.glAccountType = glAccountClassification;
this.transactionDate = transactionDate;
this.entryType = entryType;
this.amount = amount;
this.transactionId = transactionId;
this.manualEntry = manualEntry;
this.entityType = entityType;
this.entityId = entityId;
this.createdByUserId = createdByUserId;
this.createdDate = createdDate;
this.createdByUserName = createdByUserName;
this.comments = comments;
this.reversed = reversed;
this.referenceNumber = referenceNumber;
this.officeRunningBalance = officeRunningBalance;
this.organizationRunningBalance = organizationRunningBalance;
this.runningBalanceComputed = runningBalanceComputed;
this.transactionDetails = transactionDetailData;
this.currency = currency;
}
public static JournalEntryData fromGLAccountData(final GLAccountData glAccountData) {
final Long id = null;
final Long officeId = null;
final String officeName = null;
final String glAccountName = glAccountData.getName();
final Long glAccountId = glAccountData.getId();
final String glAccountCode = glAccountData.getGlCode();
final EnumOptionData glAccountClassification = glAccountData.getType();
final LocalDate transactionDate = null;
final EnumOptionData entryType = null;
final BigDecimal amount = null;
final String transactionId = null;
final Boolean manualEntry = null;
final EnumOptionData entityType = null;
final Long entityId = null;
final Long createdByUserId = null;
final LocalDate createdDate = null;
final String createdByUserName = null;
final String comments = null;
final Boolean reversed = null;
final String referenceNumber = null;
final BigDecimal officeRunningBalance = null;
final BigDecimal organizationRunningBalance = null;
final Boolean runningBalanceComputed = null;
final TransactionDetailData transactionDetailData = null;
final CurrencyData currency = null;
return new JournalEntryData(id, officeId, officeName, glAccountName, glAccountId, glAccountCode, glAccountClassification,
transactionDate, entryType, amount, transactionId, manualEntry, entityType, entityId, createdByUserId, createdDate,
createdByUserName, comments, reversed, referenceNumber, officeRunningBalance, organizationRunningBalance,
runningBalanceComputed, transactionDetailData, currency);
}
public Long getId() {
return this.id;
}
public Long getGlAccountId() {
return this.glAccountId;
}
public EnumOptionData getGlAccountType() {
return this.glAccountType;
}
public BigDecimal getAmount() {
return this.amount;
}
public EnumOptionData getEntryType() {
return this.entryType;
}
public Long getOfficeId() {
return this.officeId;
}
public String getTransactionId() {
return transactionId;
}
}
@@ -0,0 +1,56 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.reflect.TypeToken;
@Component
public class JournalEntryDataValidator {
private final FromJsonHelper fromApiJsonHelper;
public static final Set<String> RUNNING_BALANCE_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(JournalEntryJsonInputParams.OFFICE_ID.getValue()));
@Autowired
public JournalEntryDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForUpdateRunningbalance(final JsonCommand command) {
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, command.json(), RUNNING_BALANCE_UPDATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLJournalEntry");
if (this.fromApiJsonHelper.parameterExists(JournalEntryJsonInputParams.OFFICE_ID.getValue(), command.parsedJson())) {
final String officeId = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(),
command.parsedJson());
baseDataValidator.reset().parameter(JournalEntryJsonInputParams.OFFICE_ID.getValue()).value(officeId).ignoreIfNull()
.longGreaterThanZero();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -0,0 +1,48 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
/**
* Represents the successful result of an REST API call.
*/
public class JournalEntryIdentifier {
private String entityId;
// TODO - Rename variable to commandId or taskId or something that shows
// this is the id of a command in a table/queue for processing.
@SuppressWarnings("unused")
private Long makerCheckerId;
public static JournalEntryIdentifier makerChecker(final Long makerCheckerId) {
return new JournalEntryIdentifier(null, makerCheckerId);
}
public static JournalEntryIdentifier makerChecker(final String resourceId, final Long makerCheckerId) {
return new JournalEntryIdentifier(resourceId, makerCheckerId);
}
public JournalEntryIdentifier() {
//
}
public JournalEntryIdentifier(final String entityId) {
this.entityId = entityId;
}
private JournalEntryIdentifier(final String entityId, final Long makerCheckerId) {
this.entityId = entityId;
this.makerCheckerId = makerCheckerId;
}
public String getEntityId() {
return this.entityId;
}
public void setEntityId(final String entityId) {
this.entityId = entityId;
}
}
@@ -0,0 +1,91 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.util.List;
public class LoanDTO {
private Long loanId;
private Long loanProductId;
private Long officeId;
private String currencyCode;
private boolean cashBasedAccountingEnabled;
final boolean upfrontAccrualBasedAccountingEnabled;
final boolean periodicAccrualBasedAccountingEnabled;
private List<LoanTransactionDTO> newLoanTransactions;
public LoanDTO(final Long loanId, final Long loanProductId, final Long officeId, final String currencyCode,
final boolean cashBasedAccountingEnabled, final boolean upfrontAccrualBasedAccountingEnabled,
final boolean periodicAccrualBasedAccountingEnabled, final List<LoanTransactionDTO> newLoanTransactions) {
this.loanId = loanId;
this.loanProductId = loanProductId;
this.officeId = officeId;
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
this.newLoanTransactions = newLoanTransactions;
this.currencyCode = currencyCode;
this.upfrontAccrualBasedAccountingEnabled = upfrontAccrualBasedAccountingEnabled;
this.periodicAccrualBasedAccountingEnabled = periodicAccrualBasedAccountingEnabled;
}
public Long getLoanId() {
return this.loanId;
}
public void setLoanId(final Long loanId) {
this.loanId = loanId;
}
public Long getLoanProductId() {
return this.loanProductId;
}
public void setLoanProductId(final Long loanProductId) {
this.loanProductId = loanProductId;
}
public Long getOfficeId() {
return this.officeId;
}
public void setOfficeId(final Long officeId) {
this.officeId = officeId;
}
public boolean isCashBasedAccountingEnabled() {
return this.cashBasedAccountingEnabled;
}
public void setCashBasedAccountingEnabled(final boolean cashBasedAccountingEnabled) {
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
}
public boolean isUpfrontAccrualBasedAccountingEnabled() {
return this.upfrontAccrualBasedAccountingEnabled;
}
public boolean isPeriodicAccrualBasedAccountingEnabled() {
return this.periodicAccrualBasedAccountingEnabled;
}
public List<LoanTransactionDTO> getNewLoanTransactions() {
return this.newLoanTransactions;
}
public void setNewLoanTransactions(final List<LoanTransactionDTO> newLoanTransactions) {
this.newLoanTransactions = newLoanTransactions;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public void setCurrencyCode(final String currencyCode) {
this.currencyCode = currencyCode;
}
}
@@ -0,0 +1,122 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.mifosplatform.portfolio.loanaccount.data.LoanTransactionEnumData;
public class LoanTransactionDTO {
private final Long officeId;
private final String transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final LoanTransactionEnumData transactionType;
private final BigDecimal amount;
/*** Breakup of amounts in case of repayments **/
private final BigDecimal principal;
private final BigDecimal interest;
private final BigDecimal fees;
private final BigDecimal penalties;
private final BigDecimal overPayment;
/*** Boolean values determines if the transaction is reversed ***/
private final boolean reversed;
/** Breakdowns of fees and penalties this Transaction pays **/
private final List<ChargePaymentDTO> penaltyPayments;
private final List<ChargePaymentDTO> feePayments;
private final boolean isAccountTransfer;
public LoanTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
final LoanTransactionEnumData transactionType, final BigDecimal amount, final BigDecimal principal, final BigDecimal interest,
final BigDecimal fees, final BigDecimal penalties, final BigDecimal overPayment, final boolean reversed,
final List<ChargePaymentDTO> feePayments, final List<ChargePaymentDTO> penaltyPayments, boolean isAccountTransfer) {
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.principal = principal;
this.interest = interest;
this.fees = fees;
this.penalties = penalties;
this.reversed = reversed;
this.transactionType = transactionType;
this.feePayments = feePayments;
this.penaltyPayments = penaltyPayments;
this.overPayment = overPayment;
this.officeId = officeId;
this.isAccountTransfer = isAccountTransfer;
}
public Long getOfficeId() {
return this.officeId;
}
public String getTransactionId() {
return this.transactionId;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public LoanTransactionEnumData getTransactionType() {
return this.transactionType;
}
public BigDecimal getAmount() {
return this.amount;
}
public BigDecimal getPrincipal() {
return this.principal;
}
public BigDecimal getInterest() {
return this.interest;
}
public BigDecimal getFees() {
return this.fees;
}
public BigDecimal getPenalties() {
return this.penalties;
}
public BigDecimal getOverPayment() {
return this.overPayment;
}
public boolean isReversed() {
return this.reversed;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
public List<ChargePaymentDTO> getPenaltyPayments() {
return this.penaltyPayments;
}
public List<ChargePaymentDTO> getFeePayments() {
return this.feePayments;
}
public boolean isAccountTransfer() {
return this.isAccountTransfer;
}
}
@@ -0,0 +1,56 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
public class OfficeOpeningBalancesData {
@SuppressWarnings("unused")
private final Long officeId;
@SuppressWarnings("unused")
private final String officeName;
@SuppressWarnings("unused")
private final LocalDate transactionDate;
@SuppressWarnings("unused")
private final GLAccountData contraAccount;
@SuppressWarnings("unused")
private final List<JournalEntryData> assetAccountOpeningBalances;
@SuppressWarnings("unused")
private final List<JournalEntryData> liabityAccountOpeningBalances;
@SuppressWarnings("unused")
private final List<JournalEntryData> incomeAccountOpeningBalances;
@SuppressWarnings("unused")
private final List<JournalEntryData> equityAccountOpeningBalances;
@SuppressWarnings("unused")
private final List<JournalEntryData> expenseAccountOpeningBalances;
private OfficeOpeningBalancesData(final Long officeId, final String officeName, final LocalDate transactionDate,
final GLAccountData contraAccount, final List<JournalEntryData> assetAccountOpeningBalances,
final List<JournalEntryData> liabityAccountOpeningBalances, final List<JournalEntryData> incomeAccountOpeningBalances,
final List<JournalEntryData> equityAccountOpeningBalances, final List<JournalEntryData> expenseAccountOpeningBalances) {
this.officeId = officeId;
this.officeName = officeName;
this.transactionDate = transactionDate;
this.contraAccount = contraAccount;
this.assetAccountOpeningBalances = assetAccountOpeningBalances;
this.liabityAccountOpeningBalances = liabityAccountOpeningBalances;
this.incomeAccountOpeningBalances = incomeAccountOpeningBalances;
this.equityAccountOpeningBalances = equityAccountOpeningBalances;
this.expenseAccountOpeningBalances = expenseAccountOpeningBalances;
}
public static OfficeOpeningBalancesData createNew(final Long officeId, final String officeName, final LocalDate transactionDate,
final GLAccountData contraAccount, final List<JournalEntryData> assetAccountOpeningBalances,
final List<JournalEntryData> liabityAccountOpeningBalances, final List<JournalEntryData> incomeAccountOpeningBalances,
final List<JournalEntryData> equityAccountOpeningBalances, final List<JournalEntryData> expenseAccountOpeningBalances) {
return new OfficeOpeningBalancesData(officeId, officeName, transactionDate, contraAccount, assetAccountOpeningBalances,
liabityAccountOpeningBalances, incomeAccountOpeningBalances, equityAccountOpeningBalances, expenseAccountOpeningBalances);
}
}
@@ -0,0 +1,88 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.util.List;
public class SavingsDTO {
private Long savingsId;
private Long savingsProductId;
private Long officeId;
private String currencyCode;
private boolean cashBasedAccountingEnabled;
private boolean accrualBasedAccountingEnabled;
private List<SavingsTransactionDTO> newSavingsTransactions;
public SavingsDTO(final Long savingsId, final Long savingsProductId, final Long officeId, final String currencyCode,
final boolean cashBasedAccountingEnabled, final boolean accrualBasedAccountingEnabled,
final List<SavingsTransactionDTO> newSavingsTransactions) {
this.savingsId = savingsId;
this.savingsProductId = savingsProductId;
this.officeId = officeId;
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
this.newSavingsTransactions = newSavingsTransactions;
this.currencyCode = currencyCode;
}
public Long getSavingsId() {
return this.savingsId;
}
public void setSavingsId(final Long savingsId) {
this.savingsId = savingsId;
}
public Long getSavingsProductId() {
return this.savingsProductId;
}
public void setSavingsProductId(final Long savingsProductId) {
this.savingsProductId = savingsProductId;
}
public Long getOfficeId() {
return this.officeId;
}
public void setOfficeId(final Long officeId) {
this.officeId = officeId;
}
public boolean isCashBasedAccountingEnabled() {
return this.cashBasedAccountingEnabled;
}
public void setCashBasedAccountingEnabled(final boolean cashBasedAccountingEnabled) {
this.cashBasedAccountingEnabled = cashBasedAccountingEnabled;
}
public boolean isAccrualBasedAccountingEnabled() {
return this.accrualBasedAccountingEnabled;
}
public void setAccrualBasedAccountingEnabled(final boolean accrualBasedAccountingEnabled) {
this.accrualBasedAccountingEnabled = accrualBasedAccountingEnabled;
}
public List<SavingsTransactionDTO> getNewSavingsTransactions() {
return this.newSavingsTransactions;
}
public void setNewSavingsTransactions(final List<SavingsTransactionDTO> newSavingsTransactions) {
this.newSavingsTransactions = newSavingsTransactions;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public void setCurrencyCode(final String currencyCode) {
this.currencyCode = currencyCode;
}
}
@@ -0,0 +1,99 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.mifosplatform.portfolio.savings.data.SavingsAccountTransactionEnumData;
public class SavingsTransactionDTO {
private final Long officeId;
private final String transactionId;
private final Date transactionDate;
private final Long paymentTypeId;
private final SavingsAccountTransactionEnumData transactionType;
private final BigDecimal amount;
private final BigDecimal overdraftAmount;
/*** Boolean values determines if the transaction is reversed ***/
private final boolean reversed;
/** Breakdowns of fees and penalties this Transaction pays **/
private final List<ChargePaymentDTO> penaltyPayments;
private final List<ChargePaymentDTO> feePayments;
private final boolean isAccountTransfer;
public SavingsTransactionDTO(final Long officeId, final Long paymentTypeId, final String transactionId, final Date transactionDate,
final SavingsAccountTransactionEnumData transactionType, final BigDecimal amount, final boolean reversed,
final List<ChargePaymentDTO> feePayments, final List<ChargePaymentDTO> penaltyPayments, final BigDecimal overdraftAmount,
boolean isAccountTransfer) {
this.paymentTypeId = paymentTypeId;
this.transactionId = transactionId;
this.transactionDate = transactionDate;
this.amount = amount;
this.reversed = reversed;
this.transactionType = transactionType;
this.feePayments = feePayments;
this.penaltyPayments = penaltyPayments;
this.officeId = officeId;
this.overdraftAmount = overdraftAmount;
this.isAccountTransfer = isAccountTransfer;
}
public Long getOfficeId() {
return this.officeId;
}
public String getTransactionId() {
return this.transactionId;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public Long getPaymentTypeId() {
return this.paymentTypeId;
}
public SavingsAccountTransactionEnumData getTransactionType() {
return this.transactionType;
}
public BigDecimal getAmount() {
return this.amount;
}
public boolean isReversed() {
return this.reversed;
}
public List<ChargePaymentDTO> getPenaltyPayments() {
return this.penaltyPayments;
}
public List<ChargePaymentDTO> getFeePayments() {
return this.feePayments;
}
public BigDecimal getOverdraftAmount() {
return this.overdraftAmount;
}
public boolean isOverdraftTransaction() {
return this.overdraftAmount != null && this.overdraftAmount.doubleValue() > 0;
}
public boolean isAccountTransfer() {
return this.isAccountTransfer;
}
}
@@ -0,0 +1,40 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
import org.mifosplatform.portfolio.note.data.NoteData;
import org.mifosplatform.portfolio.paymentdetail.data.PaymentDetailData;
public class TransactionDetailData {
@SuppressWarnings("unused")
private final Long transactionId;
@SuppressWarnings("unused")
private final PaymentDetailData paymentDetails;
@SuppressWarnings("unused")
private final NoteData noteData;
@SuppressWarnings("unused")
private final TransactionTypeEnumData transactionType;
public TransactionDetailData(final Long transactionId, final PaymentDetailData paymentDetails, final NoteData noteData) {
this.transactionId = transactionId;
this.paymentDetails = paymentDetails;
this.noteData = noteData;
this.transactionType = null;
}
public TransactionDetailData(final Long transactionId, final PaymentDetailData paymentDetails, final NoteData noteData,
final TransactionTypeEnumData transactionType) {
this.transactionId = transactionId;
this.paymentDetails = paymentDetails;
this.noteData = noteData;
this.transactionType = transactionType;
}
}
@@ -0,0 +1,28 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.data;
/**
* Immutable data object represent loan and Savings status enumerations.
*/
@SuppressWarnings("unused")
public class TransactionTypeEnumData {
private final Long id;
private final String code;
private final String value;
public TransactionTypeEnumData(final Long id, final String code, final String value) {
this.id = id;
this.code = code;
this.value = value;
}
public Long id() {
return this.id;
}
}
@@ -0,0 +1,203 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.domain;
import java.math.BigDecimal;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
import org.mifosplatform.organisation.office.domain.Office;
import org.mifosplatform.portfolio.client.domain.ClientTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.paymentdetail.domain.PaymentDetail;
import org.mifosplatform.portfolio.savings.domain.SavingsAccountTransaction;
import org.mifosplatform.useradministration.domain.AppUser;
@Entity
@Table(name = "acc_gl_journal_entry")
public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne(optional = true)
@JoinColumn(name = "payment_details_id", nullable = true)
private PaymentDetail paymentDetail;
@ManyToOne
@JoinColumn(name = "account_id", nullable = false)
private GLAccount glAccount;
@Column(name = "currency_code", length = 3, nullable = false)
private String currencyCode;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reversal_id")
private JournalEntry reversalJournalEntry;
@Column(name = "transaction_id", nullable = false, length = 50)
private String transactionId;
@ManyToOne
@JoinColumn(name = "loan_transaction_id", nullable = false)
private LoanTransaction loanTransaction;
@ManyToOne
@JoinColumn(name = "savings_transaction_id", nullable = false)
private SavingsAccountTransaction savingsTransaction;
@ManyToOne
@JoinColumn(name = "client_transaction_id", nullable = false)
private ClientTransaction clientTransaction;
@Column(name = "reversed", nullable = false)
private boolean reversed = false;
@Column(name = "manual_entry", nullable = false)
private boolean manualEntry = false;
@Column(name = "entry_date")
@Temporal(TemporalType.DATE)
private Date transactionDate;
@Column(name = "type_enum", nullable = false)
private Integer type;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "description", length = 500)
private String description;
@Column(name = "entity_type_enum", length = 50)
private Integer entityType;
@Column(name = "entity_id")
private Long entityId;
@Column(name = "ref_num")
private String referenceNumber;
public static JournalEntry createNew(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount,
final String currencyCode, final String transactionId, final boolean manualEntry, final Date transactionDate,
final JournalEntryType journalEntryType, final BigDecimal amount, final String description, final Integer entityType,
final Long entityId, final String referenceNumber, final LoanTransaction loanTransaction,
final SavingsAccountTransaction savingsTransaction, final ClientTransaction clientTransaction) {
return new JournalEntry(office, paymentDetail, glAccount, currencyCode, transactionId, manualEntry, transactionDate,
journalEntryType.getValue(), amount, description, entityType, entityId, referenceNumber, loanTransaction,
savingsTransaction, clientTransaction);
}
protected JournalEntry() {
//
}
public JournalEntry(final Office office, final PaymentDetail paymentDetail, final GLAccount glAccount, final String currencyCode,
final String transactionId, final boolean manualEntry, final Date transactionDate, final Integer type, final BigDecimal amount,
final String description, final Integer entityType, final Long entityId, final String referenceNumber,
final LoanTransaction loanTransaction, final SavingsAccountTransaction savingsTransaction,
final ClientTransaction clientTransaction) {
this.office = office;
this.glAccount = glAccount;
this.reversalJournalEntry = null;
this.transactionId = transactionId;
this.reversed = false;
this.manualEntry = manualEntry;
this.transactionDate = transactionDate;
this.type = type;
this.amount = amount;
this.description = StringUtils.defaultIfEmpty(description, null);
this.entityType = entityType;
this.entityId = entityId;
this.referenceNumber = referenceNumber;
this.currencyCode = currencyCode;
this.loanTransaction = loanTransaction;
this.savingsTransaction = savingsTransaction;
this.clientTransaction = clientTransaction;
this.paymentDetail = paymentDetail;
}
public boolean isDebitEntry() {
return JournalEntryType.DEBIT.getValue().equals(this.type);
}
public Integer getType() {
return this.type;
}
public Office getOffice() {
return this.office;
}
public GLAccount getGlAccount() {
return this.glAccount;
}
public Date getTransactionDate() {
return this.transactionDate;
}
public BigDecimal getAmount() {
return this.amount;
}
public void setReversalJournalEntry(final JournalEntry reversalJournalEntry) {
this.reversalJournalEntry = reversalJournalEntry;
}
public void setReversed(final boolean reversed) {
this.reversed = reversed;
}
public String getReferenceNumber() {
return this.referenceNumber;
}
public String getCurrencyCode() {
return this.currencyCode;
}
public LoanTransaction getLoanTransaction() {
return this.loanTransaction;
}
public SavingsAccountTransaction getSavingsTransaction() {
return this.savingsTransaction;
}
public PaymentDetail getPaymentDetails() {
return this.paymentDetail;
}
public String getTransactionId() {
return transactionId;
}
public ClientTransaction getClientTransaction() {
return this.clientTransaction;
}
public Long getEntityId() {
return this.entityId ;
}
public Integer getEntityType() {
return this.entityType ;
}
}
@@ -0,0 +1,29 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.journalentry.domain;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long>, JpaSpecificationExecutor<JournalEntry>,
JournalEntryRepositoryCustom {
@Query("from JournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed is false and journalEntry.manualEntry is true")
List<JournalEntry> findUnReversedManualJournalEntriesByTransactionId(@Param("transactionId") String transactionId);
@Query("select DISTINCT j.transactionId from JournalEntry j where j.transactionId not in (select DISTINCT je.transactionId from JournalEntry je where je.glAccount.id = :contraId)")
List<String> findNonContraTansactionIds(@Param("contraId") Long contraId);
@Query("select DISTINCT j.transactionId from JournalEntry j where j.office.id = :officeId and j.glAccount.id = :contraId and j.reversed is false and j.transactionId not in (select DISTINCT je.reversalJournalEntry.transactionId from JournalEntry je where je.reversed is true)")
List<String> findNonReversedContraTansactionIds(@Param("contraId") Long contraId, @Param("officeId") Long officeId);
@Query("from JournalEntry journalEntry where journalEntry.entityId= :entityId and journalEntry.entityType = :entityType")
List<JournalEntry> findProvisioningJournalEntriesByEntityId(@Param("entityId") Long entityId, @Param("entityType") Integer entityType) ;
}

Some files were not shown because too many files have changed in this diff Show More