wip: adding audit for accounting

This commit is contained in:
Vishwas Babu AJ
2013-02-16 20:55:58 +05:30
parent 16ae622590
commit 7941b0dcf3
115 changed files with 2276 additions and 1955 deletions
+23
View File
@@ -0,0 +1,23 @@
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'CREATE_GLACCOUNT', 'GLACCOUNT', 'CREATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'UPDATE_GLACCOUNT', 'GLACCOUNT', 'UPDATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'DELETE_GLACCOUNT', 'GLACCOUNT', 'DELETE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'CREATE_GLCLOSURE', 'GLCLOSURE', 'CREATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'UPDATE_GLCLOSURE', 'GLCLOSURE', 'UPDATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'DELETE_GLCLOSURE', 'GLCLOSURE', 'DELETE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'CREATE_JOURNALENTRY', 'JOURNALENTRY', 'CREATE', 1);
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
VALUES ('accounting', 'REVERSE_JOURNALENTRY', 'JOURNALENTRY', 'REVERSE', 1);
@@ -1,170 +0,0 @@
/**
* 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.api;
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.Response;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiDataConversionService;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiJsonSerializerService;
import org.mifosplatform.accounting.service.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.service.GLAccountReadPlatformService;
import org.mifosplatform.accounting.service.GLAccountWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
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 {
@Autowired
private GLAccountReadPlatformService glAccountReadPlatformService;
@Autowired
private GLAccountWritePlatformService glAccountWritePlatformService;
@Autowired
private AccountingApiDataConversionService accountingApiDataConversionService;
@Autowired
private AccountingApiJsonSerializerService apiJsonSerializerService;
@Autowired
private AccountingDropdownReadPlatformService dropdownReadPlatformService;
private final String entityType = "GL_ACCOUNT";
@Autowired
private PlatformSecurityContext context;
// private final static Logger logger =
// LoggerFactory.getLogger(GLAccountsApiResource.class);
/**
* @param uriInfo
* @param classification
* @param searchParam
* Option param to search by "Name" or "GL CODE" fragments
* @return
*/
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllAccounts(@Context final UriInfo uriInfo, @QueryParam("type") final Integer classification,
@QueryParam("searchParam") final String searchParam, @QueryParam("usage") final Integer usage,
@QueryParam("manualAdjustmentsAllowed") final Boolean manualTransactionsAllowed, @QueryParam("disabled") final Boolean disabled) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final List<GLAccountData> glAccountDatas = this.glAccountReadPlatformService.retrieveAllGLAccounts(classification, searchParam,
usage, manualTransactionsAllowed, disabled);
return this.apiJsonSerializerService.serializeGLAccountDataToJson(prettyPrint, responseParameters, glAccountDatas);
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveNewLoanProductDetails(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveNewGLAccountDetails();
glAccountData = handleTemplate(glAccountData);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serializeGLAccountDataToJson(prettyPrint, responseParameters, glAccountData);
}
@GET
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveAccount(@PathParam("glAccountId") final Long glAccountId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId);
if (template) {
glAccountData = handleTemplate(glAccountData);
}
return this.apiJsonSerializerService.serializeGLAccountDataToJson(prettyPrint, responseParameters, glAccountData);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLAccount(final String jsonRequestBody) {
final GLAccountCommand command = this.accountingApiDataConversionService.convertJsonToGLAccountCommand(null, jsonRequestBody);
final Long coaId = glAccountWritePlatformService.createGLAccount(command);
return this.apiJsonSerializerService.serializeEntityIdentifier(new CommandProcessingResult(coaId));
}
@PUT
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateGLAccount(@PathParam("glAccountId") final Long glAccountId, final String jsonRequestBody) {
final GLAccountCommand command = this.accountingApiDataConversionService.convertJsonToGLAccountCommand(null, jsonRequestBody);
final Long coaId = glAccountWritePlatformService.updateGLAccount(glAccountId, command);
return this.apiJsonSerializerService.serializeEntityIdentifier(new CommandProcessingResult(coaId));
}
@DELETE
@Path("{glAccountId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response deleteGLAccount(@PathParam("glAccountId") final Long glAccountId) {
this.glAccountWritePlatformService.deleteGLAccount(glAccountId);
return Response.ok(new CommandProcessingResult(glAccountId)).build();
}
private GLAccountData handleTemplate(GLAccountData glAccountData) {
List<EnumOptionData> accountTypeOptions = dropdownReadPlatformService.retrieveGLAccountTypeOptions();
List<EnumOptionData> usageOptions = dropdownReadPlatformService.retrieveGLAccountUsageOptions();
return new GLAccountData(glAccountData, accountTypeOptions, usageOptions);
}
}
@@ -1,139 +0,0 @@
/**
* 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.api;
import java.util.ArrayList;
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.Response;
import javax.ws.rs.core.UriInfo;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
import org.mifosplatform.accounting.api.data.GLClosureData;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiDataConversionService;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiJsonSerializerService;
import org.mifosplatform.accounting.service.GLClosureReadPlatformService;
import org.mifosplatform.accounting.service.GLClosureWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
import org.mifosplatform.organisation.office.data.OfficeLookup;
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 {
@Autowired
private GLClosureReadPlatformService glClosureReadPlatformService;
@Autowired
private GLClosureWritePlatformService glClosureWritePlatformService;
@Autowired
private OfficeReadPlatformService officeReadPlatformService;
@Autowired
private AccountingApiDataConversionService accountingApiDataConversionService;
@Autowired
private AccountingApiJsonSerializerService apiJsonSerializerService;
private final String entityType = "GL_CLOSURE";
@Autowired
private PlatformSecurityContext context;
// private final static Logger logger =
// LoggerFactory.getLogger(GLClosuresApiResource.class);
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllClosures(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final List<GLClosureData> glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId);
return this.apiJsonSerializerService.serializeGLClosureDataToJson(prettyPrint, responseParameters, glClosureDatas);
}
@GET
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveClosure(@PathParam("glClosureId") final Long glClosureId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
boolean template = ApiParameterHelper.template(uriInfo.getQueryParameters());
final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
if (template) {
glClosureData.setAllowedOffices(new ArrayList<OfficeLookup>(officeReadPlatformService.retrieveAllOfficesForLookup()));
}
return this.apiJsonSerializerService.serializeGLClosureDataToJson(prettyPrint, responseParameters, glClosureData);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLClosure(final String jsonRequestBody) {
final GLClosureCommand command = this.accountingApiDataConversionService.convertJsonToGLClosureCommand(null, jsonRequestBody);
final Long coaId = glClosureWritePlatformService.createGLClosure(command);
return this.apiJsonSerializerService.serializeEntityIdentifier(new CommandProcessingResult(coaId));
}
@PUT
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String updateGLClosure(@PathParam("glClosureId") final Long glClosureId, final String jsonRequestBody) {
final GLClosureCommand command = this.accountingApiDataConversionService.convertJsonToGLClosureCommand(null, jsonRequestBody);
final Long coaId = glClosureWritePlatformService.updateGLClosure(glClosureId, command);
return this.apiJsonSerializerService.serializeEntityIdentifier(new CommandProcessingResult(coaId));
}
@DELETE
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response deleteGLClosure(@PathParam("glClosureId") final Long glClosureId) {
this.glClosureWritePlatformService.deleteGLClosure(glClosureId);
return Response.ok(new CommandProcessingResult(glClosureId)).build();
}
}
@@ -1,143 +0,0 @@
/**
* 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.api;
import java.util.Date;
import java.util.List;
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.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.data.GLJournalEntryData;
import org.mifosplatform.accounting.api.data.JournalEntryIdentifier;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiDataConversionService;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiJsonSerializerService;
import org.mifosplatform.accounting.api.infrastructure.DateParam;
import org.mifosplatform.accounting.service.GLJournalEntryReadPlatformService;
import org.mifosplatform.accounting.service.GLJournalEntryWritePlatformService;
import org.mifosplatform.infrastructure.core.api.ApiParameterHelper;
import org.mifosplatform.infrastructure.core.exception.UnrecognizedQueryParamException;
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 GLJournalEntriesApiResource {
@Autowired
private GLJournalEntryReadPlatformService glJournalEntryReadPlatformService;
@Autowired
private GLJournalEntryWritePlatformService glJournalEntryWritePlatformService;
@Autowired
private AccountingApiDataConversionService accountingApiDataConversionService;
@Autowired
private AccountingApiJsonSerializerService apiJsonSerializerService;
private final String entityType = "JOURNAL_ENTRY";
@Autowired
private PlatformSecurityContext context;
// private final static Logger logger =
// LoggerFactory.getLogger(GLJournalEntriesApiResource.class);
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllJournalEntries(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("glAccountId") final Long glAccountId, @QueryParam("portfolioGenerated") final Boolean portfolioGenerated,
@QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam,
@QueryParam("transactionId") String transactionId) {
// TODO: Vishwas Add pagination, approach at
// http://www.javaworld.com/community/node/8295 looks good
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
List<GLJournalEntryData> glJournalEntryDatas = null;
// get dates from date params
Date fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate();
}
Date toDate = null;
if (toDateParam != null) {
toDate = toDateParam.getDate();
}
if (StringUtils.isBlank(transactionId)) {
glJournalEntryDatas = this.glJournalEntryReadPlatformService.retrieveAllGLJournalEntries(officeId, glAccountId,
portfolioGenerated, fromDate, toDate);
} else {
glJournalEntryDatas = this.glJournalEntryReadPlatformService.retrieveRelatedJournalEntries(transactionId);
}
return this.apiJsonSerializerService.serializeGLJournalEntryDataToJson(prettyPrint, responseParameters, glJournalEntryDatas);
}
@GET
@Path("{glJournalEntryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveJournalEntryById(@PathParam("glJournalEntryId") final Long glJournalEntryId, @Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(entityType);
final Set<String> responseParameters = ApiParameterHelper.extractFieldsForResponseIfProvided(uriInfo.getQueryParameters());
final boolean prettyPrint = ApiParameterHelper.prettyPrint(uriInfo.getQueryParameters());
final GLJournalEntryData glJournalEntryData = this.glJournalEntryReadPlatformService.retrieveGLJournalEntryById(glJournalEntryId);
return this.apiJsonSerializerService.serializeGLJournalEntryDataToJson(prettyPrint, responseParameters, glJournalEntryData);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createGLJournalEntry(final String jsonRequestBody) {
final GLJournalEntryCommand command = this.accountingApiDataConversionService.convertJsonToGLJournalEntryCommand(jsonRequestBody);
final String transactionId = glJournalEntryWritePlatformService.createJournalEntry(command);
return this.apiJsonSerializerService.serializeJournalEntryIdentifier(new JournalEntryIdentifier(transactionId));
}
@POST
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createReversalJournalEntry(@PathParam("transactionId") final String transactionId,
@QueryParam("command") final String commandParam) {
if (is(commandParam, "reverse")) {
this.glJournalEntryWritePlatformService.revertJournalEntry(transactionId);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.apiJsonSerializerService.serializeJournalEntryIdentifier(new JournalEntryIdentifier(transactionId));
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -1,115 +0,0 @@
/**
* 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.api.commands;
import java.util.Set;
import org.mifosplatform.accounting.domain.GLAccountUsage;
/**
* Immutable command for adding a general Ledger Account
*/
public class GLAccountCommand {
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 classification;
private final String description;
private final Set<String> parametersPassedInRequest;
public GLAccountCommand(final Set<String> modifiedParameters, final Long id, final String name, final Long parentId,
final String glCode, final Boolean disabled, final Boolean manualEntriesAllowed, final Integer classification,
final Integer usage, final String description) {
this.parametersPassedInRequest = modifiedParameters;
this.id = id;
this.name = name;
this.parentId = parentId;
this.glCode = glCode;
this.disabled = disabled;
this.manualEntriesAllowed = manualEntriesAllowed;
this.classification = classification;
this.usage = usage;
this.description = description;
}
public boolean isHeaderAccount() {
return GLAccountUsage.HEADER.getValue().equals(this.usage);
}
public boolean isNameChanged() {
return this.parametersPassedInRequest.contains("name");
}
public boolean isParentIdChanged() {
return this.parametersPassedInRequest.contains("parentId");
}
public boolean isGLCodeChanged() {
return this.parametersPassedInRequest.contains("glCode");
}
public boolean isDisabledFlagChanged() {
return this.parametersPassedInRequest.contains("disabled");
}
public boolean isManualEntriesAllowedFlagChanged() {
return this.parametersPassedInRequest.contains("manualEntriesAllowed");
}
public boolean isClassificationChanged() {
return this.parametersPassedInRequest.contains("classification");
}
public boolean isUsageChanged() {
return this.parametersPassedInRequest.contains("usage");
}
public boolean isDescriptionChanged() {
return this.parametersPassedInRequest.contains("description");
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public Long getParentId() {
return parentId;
}
public String getGlCode() {
return glCode;
}
public Boolean getDisabled() {
return disabled;
}
public Boolean getManualEntriesAllowed() {
return manualEntriesAllowed;
}
public Integer getUsage() {
return this.usage;
}
public String getDescription() {
return description;
}
public Integer getClassification() {
return classification;
}
}
@@ -1,53 +0,0 @@
/**
* 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.api.commands;
import java.util.Set;
import org.joda.time.LocalDate;
/**
* Immutable command for adding an accounting closure
*/
public class GLClosureCommand {
private final Long id;
private final Long officeId;
private final LocalDate closingDate;
private final String comments;
private final Set<String> parametersPassedInRequest;
public GLClosureCommand(final Set<String> modifiedParameters, final Long id, final Long officeId, final LocalDate closingDate,
final String comments) {
this.id = id;
this.officeId = officeId;
this.closingDate = closingDate;
this.comments = comments;
this.parametersPassedInRequest = modifiedParameters;
}
public boolean isCommentsChanged() {
return this.parametersPassedInRequest.contains("comments");
}
public Long getId() {
return this.id;
}
public Long getOfficeId() {
return this.officeId;
}
public LocalDate getClosingDate() {
return this.closingDate;
}
public String getComments() {
return this.comments;
}
}
@@ -1,76 +0,0 @@
/**
* 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.api.commands;
import java.util.Set;
import org.joda.time.LocalDate;
/**
* Immutable command for adding an accounting closure
*/
public class GLJournalEntryCommand {
private final Long officeId;
private final LocalDate entryDate;
private final String comments;
private final SingleDebitOrCreditEntryCommand[] credits;
private final SingleDebitOrCreditEntryCommand[] debits;
private final Set<String> parametersPassedInRequest;
public GLJournalEntryCommand(Set<String> parametersPassedInRequest, Long officeId, LocalDate entryDate, String comments,
SingleDebitOrCreditEntryCommand[] credits, SingleDebitOrCreditEntryCommand[] debits) {
this.officeId = officeId;
this.entryDate = entryDate;
this.comments = comments;
this.credits = credits;
this.debits = debits;
this.parametersPassedInRequest = parametersPassedInRequest;
}
public boolean isOfficeIdChanged() {
return this.parametersPassedInRequest.contains("officeId");
}
public boolean isEntryDateChanged() {
return this.parametersPassedInRequest.contains("entryDate");
}
public boolean isCommentsChanged() {
return this.parametersPassedInRequest.contains("comments");
}
public boolean isCreditsChanged() {
return this.parametersPassedInRequest.contains("credits");
}
public boolean isDebitsChanged() {
return this.parametersPassedInRequest.contains("debits");
}
public Long getOfficeId() {
return this.officeId;
}
public LocalDate getEntryDate() {
return this.entryDate;
}
public String getComments() {
return this.comments;
}
public SingleDebitOrCreditEntryCommand[] getCredits() {
return this.credits;
}
public SingleDebitOrCreditEntryCommand[] getDebits() {
return this.debits;
}
}
@@ -1,11 +0,0 @@
/**
* 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.api.commands;
public class ProductToGLAccountMappingCommand {
}
@@ -1,20 +0,0 @@
/**
* 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.api.infrastructure;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
public interface AccountingApiDataConversionService {
GLAccountCommand convertJsonToGLAccountCommand(Long resourceIdentifier, String json);
GLClosureCommand convertJsonToGLClosureCommand(Long resourceIdentifier, String json);
GLJournalEntryCommand convertJsonToGLJournalEntryCommand(String json);
}
@@ -1,37 +0,0 @@
/**
* 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.api.infrastructure;
import java.util.Collection;
import java.util.Set;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.api.data.GLClosureData;
import org.mifosplatform.accounting.api.data.GLJournalEntryData;
import org.mifosplatform.accounting.api.data.JournalEntryIdentifier;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface AccountingApiJsonSerializerService {
String serializeEntityIdentifier(final CommandProcessingResult identifier);
String serializeJournalEntryIdentifier(final JournalEntryIdentifier identifier);
String serializeGLAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, GLAccountData accountData);
String serializeGLAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GLAccountData> accountDatas);
String serializeGLClosureDataToJson(boolean prettyPrint, Set<String> responseParameters, GLClosureData closureData);
String serializeGLClosureDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GLClosureData> closureDatas);
String serializeGLJournalEntryDataToJson(boolean prettyPrint, Set<String> responseParameters, GLJournalEntryData journalEntryData);
String serializeGLJournalEntryDataToJson(boolean prettyPrint, Set<String> responseParameters,
Collection<GLJournalEntryData> journalEntryDatas);
}
@@ -1,178 +0,0 @@
/**
* 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.api.infrastructure.impl;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.commands.SingleDebitOrCreditEntryCommand;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiDataConversionService;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
import org.mifosplatform.infrastructure.core.exception.UnsupportedParameterException;
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
import org.springframework.stereotype.Service;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
/**
* Service specifically for converting JSON requests into command objects for
* accounting API.
*/
@Service
public class AccountingApiDataConversionServiceImpl implements AccountingApiDataConversionService {
private final Gson gsonConverter;
public AccountingApiDataConversionServiceImpl() {
gsonConverter = new Gson();
}
private void checkForUnsupportedParameters(Map<String, ?> requestMap, Set<String> supportedParams) {
List<String> unsupportedParameterList = new ArrayList<String>();
for (String providedParameter : requestMap.keySet()) {
if (!supportedParams.contains(providedParameter)) {
unsupportedParameterList.add(providedParameter);
}
}
if (!unsupportedParameterList.isEmpty()) { throw new UnsupportedParameterException(unsupportedParameterList); }
}
@Override
public GLAccountCommand convertJsonToGLAccountCommand(final Long resourceIdentifier, final String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(Arrays.asList("name", "parentId", "glCode", "disabled",
"manualEntriesAllowed", "classification", "usage", "description", "locale", "dateFormat"));
checkForUnsupportedParameters(requestMap, supportedParams);
final JsonParser parser = new JsonParser();
final JsonElement element = parser.parse(json);
final JsonParserHelper helper = new JsonParserHelper();
final Set<String> requestParamatersDetected = new HashSet<String>();
final String name = helper.extractStringNamed("name", element, requestParamatersDetected);
final Long parentId = helper.extractLongNamed("parentId", element, requestParamatersDetected);
final String glCode = helper.extractStringNamed("glCode", element, requestParamatersDetected);
final Boolean disabled = helper.extractBooleanNamed("disabled", element, requestParamatersDetected);
final Boolean manualEntriesAllowed = helper.extractBooleanNamed("manualEntriesAllowed", element, requestParamatersDetected);
final Integer category = helper.extractIntegerSansLocaleNamed("classification", element, requestParamatersDetected);
final Integer usage = helper.extractIntegerSansLocaleNamed("usage", element, requestParamatersDetected);
final String description = helper.extractStringNamed("description", element, requestParamatersDetected);
return new GLAccountCommand(requestParamatersDetected, resourceIdentifier, name, parentId, glCode, disabled, manualEntriesAllowed,
category, usage, description);
}
@Override
public GLClosureCommand convertJsonToGLClosureCommand(Long resourceIdentifier, String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(
Arrays.asList("officeId", "closingDate", "comments", "locale", "dateFormat"));
checkForUnsupportedParameters(requestMap, supportedParams);
final JsonParser parser = new JsonParser();
final JsonElement element = parser.parse(json);
final JsonParserHelper helper = new JsonParserHelper();
final Set<String> requestParamatersDetected = new HashSet<String>();
final Long officeId = helper.extractLongNamed("officeId", element, requestParamatersDetected);
final String comments = helper.extractStringNamed("comments", element, requestParamatersDetected);
final LocalDate closingDate = helper.extractLocalDateNamed("closingDate", element, requestParamatersDetected);
return new GLClosureCommand(requestParamatersDetected, resourceIdentifier, officeId, closingDate, comments);
}
@Override
public GLJournalEntryCommand convertJsonToGLJournalEntryCommand(String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Map<String, Object> requestMap = gsonConverter.fromJson(json, typeOfMap);
final Set<String> supportedParams = new HashSet<String>(Arrays.asList("officeId", "entryDate", "comments", "credits", "debits",
"locale", "dateFormat"));
checkForUnsupportedParameters(requestMap, supportedParams);
final JsonParser parser = new JsonParser();
final JsonElement element = parser.parse(json);
final JsonParserHelper helper = new JsonParserHelper();
final Set<String> requestParamatersDetected = new HashSet<String>();
final Long officeId = helper.extractLongNamed("officeId", element, requestParamatersDetected);
final String comments = helper.extractStringNamed("comments", element, requestParamatersDetected);
final LocalDate entryDate = helper.extractLocalDateNamed("entryDate", element, requestParamatersDetected);
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = helper.extractLocaleParameter(topLevelJsonElement);
SingleDebitOrCreditEntryCommand[] credits = null;
SingleDebitOrCreditEntryCommand[] debits = null;
if (element.isJsonObject()) {
if (topLevelJsonElement.has("credits") && topLevelJsonElement.get("credits").isJsonArray()) {
credits = populateCreditsOrDebitsArray(topLevelJsonElement, helper, requestParamatersDetected, locale, credits, "credits");
}
if (topLevelJsonElement.has("debits") && topLevelJsonElement.get("debits").isJsonArray()) {
debits = populateCreditsOrDebitsArray(topLevelJsonElement, helper, requestParamatersDetected, locale, debits, "debits");
}
}
return new GLJournalEntryCommand(requestParamatersDetected, officeId, entryDate, comments, credits, debits);
}
/**
* @param helper
* @param requestParamatersDetected
* @param comments
* @param topLevelJsonElement
* @param locale
*/
private SingleDebitOrCreditEntryCommand[] populateCreditsOrDebitsArray(final JsonObject topLevelJsonElement,
final JsonParserHelper helper, final Set<String> requestParamatersDetected, final Locale locale,
SingleDebitOrCreditEntryCommand[] debitOrCredits, String paramName) {
requestParamatersDetected.add(paramName);
final JsonArray array = topLevelJsonElement.get(paramName).getAsJsonArray();
debitOrCredits = new SingleDebitOrCreditEntryCommand[array.size()];
for (int i = 0; i < array.size(); i++) {
final JsonObject creditElement = array.get(i).getAsJsonObject();
final Set<String> parametersPassedInForCreditsCommand = new HashSet<String>();
final Long glAccountId = helper.extractLongNamed("glAccountId", creditElement, parametersPassedInForCreditsCommand);
final String comments = helper.extractStringNamed("comments", creditElement, parametersPassedInForCreditsCommand);
final BigDecimal amount = helper.extractBigDecimalNamed("amount", creditElement, locale, parametersPassedInForCreditsCommand);
debitOrCredits[i] = new SingleDebitOrCreditEntryCommand(parametersPassedInForCreditsCommand, glAccountId, amount, comments);
}
return debitOrCredits;
}
}
@@ -1,112 +0,0 @@
/**
* 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.api.infrastructure.impl;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.api.data.GLClosureData;
import org.mifosplatform.accounting.api.data.GLJournalEntryData;
import org.mifosplatform.accounting.api.data.JournalEntryIdentifier;
import org.mifosplatform.accounting.api.infrastructure.AccountingApiJsonSerializerService;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.mifosplatform.portfolio.savingsaccount.PortfolioApiJsonSerializerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.google.gson.Gson;
/**
* Implementation of {@link PortfolioApiJsonSerializerService} that uses
* google-gson to serialize Java object representation into JSON.
*/
@Service
public class GoogleGsonAccountingApiJsonSerializerService implements AccountingApiJsonSerializerService {
private static final Set<String> DATA_PARAMETERS = new HashSet<String>(Arrays.asList("entityId"));
private static final Set<String> GL_ACCOUNT_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "name", "parentId", "glCode",
"disabled", "manualEntriesAllowed", "classification", "headerAccount", "description"));
private static final Set<String> GL_ACCOUNT_CLOSURE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
"closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId", "createdByUsername", "lastUpdatedByUserId",
"lastUpdatedByUsername", "comments", "allowedOffices"));
private static final Set<String> GL_JOURNAL_ENTRY_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
"glAccountName", "glAccountId", "glAccountCode", "glAccountClassification", "entryDate", "entryType", "amount",
"transactionId", "portfolioGenerated", "entityType", "entityId", "createdByUserId", "createdDate", "createdByUserName",
"comments", "reversed"));
private final GoogleGsonSerializerHelper helper;
@Autowired
public GoogleGsonAccountingApiJsonSerializerService(final GoogleGsonSerializerHelper helper) {
this.helper = helper;
}
@Override
public String serializeEntityIdentifier(final CommandProcessingResult identifier) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(DATA_PARAMETERS, false,
DATA_PARAMETERS);
return helper.serializedJsonFrom(gsonDeserializer, identifier);
}
@Override
public String serializeJournalEntryIdentifier(final JournalEntryIdentifier identifier) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(DATA_PARAMETERS, false,
DATA_PARAMETERS);
return helper.serializedJsonFrom(gsonDeserializer, identifier);
}
@Override
public String serializeGLAccountDataToJson(final boolean prettyPrint, final Set<String> responseParameters,
final GLAccountData accountData) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(GL_ACCOUNT_DATA_PARAMETERS,
prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, accountData);
}
@Override
public String serializeGLAccountDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GLAccountData> accountDatas) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(GL_ACCOUNT_DATA_PARAMETERS,
prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, accountDatas.toArray(new GLAccountData[accountDatas.size()]));
}
@Override
public String serializeGLClosureDataToJson(boolean prettyPrint, Set<String> responseParameters, GLClosureData closureData) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(
GL_ACCOUNT_CLOSURE_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, closureData);
}
@Override
public String serializeGLClosureDataToJson(boolean prettyPrint, Set<String> responseParameters, Collection<GLClosureData> closureDatas) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(
GL_ACCOUNT_CLOSURE_DATA_PARAMETERS, prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, closureDatas.toArray(new GLClosureData[closureDatas.size()]));
}
@Override
public String serializeGLJournalEntryDataToJson(boolean prettyPrint, Set<String> responseParameters, GLJournalEntryData journalEntryData) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(GL_JOURNAL_ENTRY_DATA_PARAMETERS,
prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, journalEntryData);
}
@Override
public String serializeGLJournalEntryDataToJson(boolean prettyPrint, Set<String> responseParameters,
Collection<GLJournalEntryData> journalEntryDatas) {
final Gson gsonDeserializer = helper.createGsonBuilderWithParameterExclusionSerializationStrategy(GL_JOURNAL_ENTRY_DATA_PARAMETERS,
prettyPrint, responseParameters);
return helper.serializedJsonFrom(gsonDeserializer, journalEntryDatas.toArray(new GLJournalEntryData[journalEntryDatas.size()]));
}
}
@@ -0,0 +1,37 @@
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<String>();
static {
for (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,140 @@
/**
* 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.ArrayList;
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.data.OfficeLookup;
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<String>(Arrays.asList("id", "officeId", "officeName",
"closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId", "createdByUsername", "lastUpdatedByUserId",
"lastUpdatedByUsername"));
private final String resourceNameForPermission = "GLCLOSURE";
private GLClosureReadPlatformService glClosureReadPlatformService;
private 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) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
final List<GLClosureData> glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId);
final ApiRequestJsonSerializationSettings settings = 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) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
if (settings.isTemplate()) {
glClosureData.setAllowedOffices(new ArrayList<OfficeLookup>(officeReadPlatformService.retrieveAllOfficesForLookup()));
}
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);
}
}
@@ -3,22 +3,33 @@
* 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.service;
package org.mifosplatform.accounting.closure.command;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
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;
public class GLClosureCommandValidator {
/**
* Immutable command for adding an accounting closure
*/
public class GLClosureCommand {
private final GLClosureCommand command;
@SuppressWarnings("unused")
private final Long id;
private final Long officeId;
private final LocalDate closingDate;
private final String comments;
public GLClosureCommandValidator(GLClosureCommand command) {
this.command = command;
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() {
@@ -27,11 +38,11 @@ public class GLClosureCommandValidator {
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter("closingDate").value(command.getClosingDate()).notBlank();
baseDataValidator.reset().parameter("officeId").value(command.getOfficeId()).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("comments").value(command.getComments()).ignoreIfNull().notExceedingLengthOf(500);
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); }
@@ -42,9 +53,9 @@ public class GLClosureCommandValidator {
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter("comments").value(command.getComments()).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().anyOfNotNull(command.getComments());
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); }
@@ -3,7 +3,7 @@
* 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.api.data;
package org.mifosplatform.accounting.closure.data;
import java.util.ArrayList;
import java.util.List;
@@ -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.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.portfolio.loanaccount.guarantor.GuarantorConstants.GUARANTOR_JSON_INPUT_PARAMS;
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;
@SuppressWarnings("unused")
@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;
public static GLClosure createNew(Office office, Date closingDate, String comments) {
return new GLClosure(office, closingDate, comments);
}
protected GLClosure() {
//
}
public GLClosure(Office office, Date closingDate, 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(Office office, final JsonCommand command) {
Date closingDate = command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
String comments = command.stringValueOfParameterNamed(GUARANTOR_JSON_INPUT_PARAMS.COMMENT.getValue());
return new GLClosure(office, closingDate, comments);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(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,
String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
propertyToBeUpdated = newValue;
// now update actual property
if (paramName.equals(GUARANTOR_JSON_INPUT_PARAMS.COMMENT.getValue())) {
this.comments = newValue;
}
}
}
public Date getClosingDate() {
return this.closingDate;
}
public Office getOffice() {
return this.office;
}
}
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.closure.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.closure.exception;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.closure.exception;
import java.util.Date;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.closure.exception;
import java.util.Date;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.closure.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
@@ -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.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
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
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,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.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
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
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,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.closure.handler;
import org.mifosplatform.accounting.closure.service.GLClosureWritePlatformService;
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
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(String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
Set<String> supportedParameters = GLClosureJsonInputParams.getAllValues();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final Long id = fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.ID.getValue(), element);
final Long officeId = fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.OFFICE_ID.getValue(), element);
final String comments = fromApiJsonHelper.extractStringNamed(GLClosureJsonInputParams.COMMENTS.getValue(), element);
final LocalDate closingDate = fromApiJsonHelper
.extractLocalDateNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue(), element);
return new GLClosureCommand(id, officeId, closingDate, comments);
}
}
@@ -3,11 +3,11 @@
* 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.service;
package org.mifosplatform.accounting.closure.service;
import java.util.List;
import org.mifosplatform.accounting.api.data.GLClosureData;
import org.mifosplatform.accounting.closure.data.GLClosureData;
public interface GLClosureReadPlatformService {
@@ -3,7 +3,7 @@
* 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.service.impl;
package org.mifosplatform.accounting.closure.service;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -11,9 +11,8 @@ import java.util.Arrays;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.api.data.GLClosureData;
import org.mifosplatform.accounting.exceptions.GLClosureNotFoundException;
import org.mifosplatform.accounting.service.GLClosureReadPlatformService;
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.TenantAwareRoutingDataSource;
import org.springframework.beans.factory.annotation.Autowired;
@@ -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);
}
@@ -3,21 +3,25 @@
* 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.service.impl;
package org.mifosplatform.accounting.closure.service;
import java.util.Date;
import java.util.Map;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
import org.mifosplatform.accounting.domain.GLClosure;
import org.mifosplatform.accounting.domain.GLClosureRepository;
import org.mifosplatform.accounting.exceptions.GLClosureDuplicateException;
import org.mifosplatform.accounting.exceptions.GLClosureInvalidDeleteException;
import org.mifosplatform.accounting.exceptions.GLClosureInvalidException;
import org.mifosplatform.accounting.exceptions.GLClosureInvalidException.GL_CLOSURE_INVALID_REASON;
import org.mifosplatform.accounting.exceptions.GLClosureNotFoundException;
import org.mifosplatform.accounting.service.GLClosureCommandValidator;
import org.mifosplatform.accounting.service.GLClosureWritePlatformService;
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;
@@ -36,66 +40,74 @@ public class GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosure
private final GLClosureRepository glClosureRepository;
private final OfficeRepository officeRepository;
private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Autowired
public GLClosureWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final OfficeRepository officeRepository) {
final OfficeRepository officeRepository, GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@Transactional
@Override
public Long createGLClosure(GLClosureCommand command) {
public CommandProcessingResult createGLClosure(JsonCommand command) {
try {
GLClosureCommandValidator validator = new GLClosureCommandValidator(command);
validator.validateForCreate();
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForCreate();
// check office is valid
final Office office = this.officeRepository.findOne(command.getOfficeId());
if (office == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
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
Date todaysDate = new Date();
Date closureDate = command.getClosingDate().toDateMidnight().toDate();
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
GLClosure latestGLClosure = glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
GLClosure latestGLClosure = glClosureRepository.getLatestGLClosureByBranch(officeId);
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
}
GLClosure glClosure = GLClosure.createNew(office, command.getClosingDate(), command.getComments());
GLClosure glClosure = GLClosure.fromJson(office, command);
this.glClosureRepository.saveAndFlush(glClosure);
return glClosure.getId();
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withEntityId(glClosure.getId()).build();
} catch (DataIntegrityViolationException dve) {
handleGLClosureIntegrityIssues(command, dve);
return Long.valueOf(-1);
return CommandProcessingResult.empty();
}
}
@Transactional
@Override
public Long updateGLClosure(Long glClosureId, GLClosureCommand command) {
GLClosureCommandValidator validator = new GLClosureCommandValidator(command);
validator.validateForUpdate();
public CommandProcessingResult updateGLClosure(Long glClosureId, JsonCommand command) {
final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
closureCommand.validateForUpdate();
// is the glClosure valid
GLClosure glClosure = glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
glClosure.update(command);
final Map<String, Object> changesOnly = glClosure.update(command);
this.glClosureRepository.saveAndFlush(glClosure);
if (!changesOnly.isEmpty()) {
this.glClosureRepository.saveAndFlush(glClosure);
}
return glClosure.getId();
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(glClosure.getOffice().getId())
.withEntityId(glClosure.getId()).build();
}
@Transactional
@Override
public Long deleteGLClosure(Long glClosureId) {
public CommandProcessingResult deleteGLClosure(Long glClosureId) {
final GLClosure glClosure = this.glClosureRepository.findOne(glClosureId);
if (glClosure == null) { throw new GLClosureNotFoundException(glClosureId); }
@@ -111,17 +123,18 @@ public class GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosure
this.glClosureRepository.delete(glClosure);
return glClosureId;
return new CommandProcessingResultBuilder().withOfficeId(glClosure.getOffice().getId()).withEntityId(glClosure.getId()).build();
}
/**
* @param command
* @param dve
*/
private void handleGLClosureIntegrityIssues(final GLClosureCommand command, DataIntegrityViolationException dve) {
private void handleGLClosureIntegrityIssues(final JsonCommand command, DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("office_id_closing_date")) { throw new GLClosureDuplicateException(command.getOfficeId(),
command.getClosingDate()); }
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",
@@ -3,7 +3,7 @@
* 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;
package org.mifosplatform.accounting.common;
import java.util.HashMap;
import java.util.Map;
@@ -3,7 +3,7 @@
* 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.service;
package org.mifosplatform.accounting.common;
import java.util.List;
@@ -3,15 +3,13 @@
* 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.service.impl;
package org.mifosplatform.accounting.common;
import java.util.List;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.GLAccountUsage;
import org.mifosplatform.accounting.domain.JournalEntryType;
import org.mifosplatform.accounting.service.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.service.AccountingEnumerations;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.springframework.stereotype.Service;
@@ -3,15 +3,15 @@
* 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.service;
package org.mifosplatform.accounting.common;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.GLAccountUsage;
import org.mifosplatform.accounting.domain.JournalEntryType;
import org.mifosplatform.accounting.domain.PortfolioProductType;
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 {
@@ -1,148 +0,0 @@
/**
* 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.domain;
import java.util.LinkedList;
import java.util.List;
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.api.commands.GLAccountCommand;
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> {
@SuppressWarnings("unused")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private GLAccount parent;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private List<GLAccount> children = new LinkedList<GLAccount>();
@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 classification;
@Column(name = "account_usage", nullable = false)
private Integer usage;
@SuppressWarnings("unused")
@Column(name = "description", nullable = true, length = 500)
private String description;
public static GLAccount createNew(final GLAccount parent, final String name, final String glCode, final boolean disabled,
final boolean manualEntriesAllowed, final Integer classification, final Integer usage, final String description) {
return new GLAccount(parent, name, glCode, disabled, manualEntriesAllowed, classification, usage, description);
}
protected GLAccount() {
//
}
private GLAccount(final GLAccount parent, final String name, final String glCode, final boolean disabled,
final boolean manualEntriesAllowed, final Integer classification, final Integer usage, final String description) {
this.parent = parent;
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.classification = classification;
this.description = StringUtils.defaultIfEmpty(description, null);
}
public void update(final GLAccountCommand command, final GLAccount parentGLAccount) {
if (command.isClassificationChanged()) {
this.classification = command.getClassification();
}
if (command.isDescriptionChanged()) {
this.description = command.getDescription();
}
if (command.isDisabledFlagChanged()) {
this.disabled = command.getDisabled();
}
if (command.isGLCodeChanged()) {
this.glCode = command.getGlCode();
}
if (command.isUsageChanged()) {
this.usage = command.getUsage();
}
if (command.isManualEntriesAllowedFlagChanged()) {
this.manualEntriesAllowed = command.getManualEntriesAllowed();
}
if (command.isNameChanged()) {
this.name = command.getName();
}
if (command.isParentIdChanged()) {
this.parent = parentGLAccount;
}
}
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 void setGlCode(String glCode) {
this.glCode = glCode;
}
public String getName() {
return this.name;
}
public Integer getClassification() {
return this.classification;
}
public void setClassification(Integer classification) {
this.classification = classification;
}
}
@@ -1,79 +0,0 @@
/**
* 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.domain;
import java.util.Date;
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.joda.time.LocalDate;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
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;
@SuppressWarnings("unused")
@Column(name = "is_deleted", nullable = false)
private boolean deleted = true;
@Column(name = "closing_date")
@Temporal(TemporalType.DATE)
private Date closingDate;
@SuppressWarnings("unused")
@Column(name = "comments", nullable = true, length = 500)
private String comments;
public static GLClosure createNew(Office office, LocalDate closingDate, String comments) {
return new GLClosure(office, closingDate, comments);
}
protected GLClosure() {
//
}
public GLClosure(Office office, LocalDate closingDate, String comments) {
this.office = office;
this.deleted = false;
this.closingDate = closingDate.toDateMidnight().toDate();
if (StringUtils.isNotBlank(comments)) {
this.comments = comments.trim();
} else {
this.comments = null;
}
}
public void update(final GLClosureCommand command) {
if (command.isCommentsChanged()) {
this.comments = command.getComments().trim();
}
}
public Date getClosingDate() {
return this.closingDate;
}
public Office getOffice() {
return this.office;
}
}
@@ -1,20 +0,0 @@
/**
* 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.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 GLJournalEntryRepository extends JpaRepository<GLJournalEntry, Long>, JpaSpecificationExecutor<GLJournalEntry>,
GLJournalEntryRepositoryCustom {
@Query("from GLJournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed is false and journalEntry.portfolioGenerated is false")
List<GLJournalEntry> findUnReversedManualJournalEntriesByTransactionId(@Param("transactionId") String transactionId);
}
@@ -0,0 +1,38 @@
package org.mifosplatform.accounting.glaccount.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a loan product
***/
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");
private final String value;
private GLAccountJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (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,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.glaccount.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.common.AccountingDropdownReadPlatformService;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService;
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.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<String>(Arrays.asList("id", "name", "parentId", "glCode",
"disabled", "manualEntriesAllowed", "type", "usage", "description"));
private final String resourceNameForPermission = "GLACCOUNT";
private GLAccountReadPlatformService glAccountReadPlatformService;
private AccountingDropdownReadPlatformService dropdownReadPlatformService;
private final DefaultToApiJsonSerializer<GLAccountData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public GLAccountsApiResource(final PlatformSecurityContext context, final GLAccountReadPlatformService glAccountReadPlatformService,
final DefaultToApiJsonSerializer<GLAccountData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final AccountingDropdownReadPlatformService dropdownReadPlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.glAccountReadPlatformService = glAccountReadPlatformService;
this.dropdownReadPlatformService = dropdownReadPlatformService;
}
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveNewAccountDetails(@Context final UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveNewGLAccountDetails();
glAccountData = handleTemplate(glAccountData);
final ApiRequestJsonSerializationSettings settings = 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) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
final List<GLAccountData> glAccountDatas = this.glAccountReadPlatformService.retrieveAllGLAccounts(type, searchParam, usage,
manualEntriesAllowed, disabled);
final ApiRequestJsonSerializationSettings settings = 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) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
GLAccountData glAccountData = this.glAccountReadPlatformService.retrieveGLAccountById(glAccountId);
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(GLAccountData glAccountData) {
List<EnumOptionData> accountTypeOptions = dropdownReadPlatformService.retrieveGLAccountTypeOptions();
List<EnumOptionData> usageOptions = dropdownReadPlatformService.retrieveGLAccountUsageOptions();
return new GLAccountData(glAccountData, accountTypeOptions, usageOptions);
}
}
@@ -0,0 +1,112 @@
/**
* 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;
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) {
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;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).notBlank()
.notExceedingLengthOf(45);
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();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter(GLAccountJsonInputParams.NAME.getValue()).value(this.name).ignoreIfNull().notBlank()
.notExceedingLengthOf(45);
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); }
}
public boolean isHeaderAccount() {
return GLAccountUsage.HEADER.getValue().equals(this.usage);
}
}
@@ -3,13 +3,13 @@
* 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.api.data;
package org.mifosplatform.accounting.glaccount.data;
import java.util.List;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.GLAccountUsage;
import org.mifosplatform.accounting.service.AccountingEnumerations;
import org.mifosplatform.accounting.common.AccountingEnumerations;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
/**
@@ -3,7 +3,7 @@
* 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.api.data;
package org.mifosplatform.accounting.glaccount.data;
import java.util.List;
@@ -73,4 +73,4 @@ public class LoanDTO {
public void setNewLoanTransactions(final List<LoanTransactionDTO> newLoanTransactions) {
this.newLoanTransactions = newLoanTransactions;
}
}
}
@@ -3,7 +3,7 @@
* 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.api.data;
package org.mifosplatform.accounting.glaccount.data;
import java.math.BigDecimal;
import java.util.Date;
@@ -0,0 +1,215 @@
/**
* 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.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> {
@SuppressWarnings("unused")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private GLAccount parent;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
private List<GLAccount> children = new LinkedList<GLAccount>();
@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;
public static GLAccount createNew(final GLAccount parent, final String name, final String glCode, final boolean disabled,
final boolean manualEntriesAllowed, final Integer type, final Integer usage, final String description) {
return new GLAccount(parent, name, glCode, disabled, manualEntriesAllowed, type, usage, description);
}
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) {
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;
}
public static GLAccount fromJson(GLAccount parent, final JsonCommand command) {
String name = command.stringValueOfParameterNamed(GLAccountJsonInputParams.NAME.getValue());
String glCode = command.stringValueOfParameterNamed(GLAccountJsonInputParams.GL_CODE.getValue());
boolean disabled = command.booleanPrimitiveValueOfParameterNamed(GLAccountJsonInputParams.DISABLED.getValue());
boolean manualEntriesAllowed = command.booleanPrimitiveValueOfParameterNamed(GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED
.getValue());
Integer usage = command.integerValueSansLocaleOfParameterNamed(GLAccountJsonInputParams.USAGE.getValue());
Integer type = command.integerValueSansLocaleOfParameterNamed(GLAccountJsonInputParams.TYPE.getValue());
String description = command.stringValueOfParameterNamed(GLAccountJsonInputParams.DESCRIPTION.getValue());
return new GLAccount(parent, name, glCode, disabled, manualEntriesAllowed, type, usage, description);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(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);
return actualChanges;
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
Integer propertyToBeUpdated, 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);
propertyToBeUpdated = 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,
String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
propertyToBeUpdated = 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,
Long propertyToBeUpdated) {
if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) {
final Long newValue = command.longValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
propertyToBeUpdated = 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,
boolean propertyToBeUpdated) {
if (command.isChangeInBooleanParameterNamed(paramName, propertyToBeUpdated)) {
final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
propertyToBeUpdated = 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;
}
}
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.glaccount.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.glaccount.domain;
import java.util.HashMap;
import java.util.Map;
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.glaccount.domain;
import java.util.HashMap;
import java.util.Map;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.glaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
@@ -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.glaccount.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
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
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,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.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
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
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,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.handler;
import org.mifosplatform.accounting.glaccount.service.GLAccountWritePlatformService;
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
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,63 @@
/**
* 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(String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
Set<String> supportedParameters = GLAccountJsonInputParams.getAllValues();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final Long id = fromApiJsonHelper.extractLongNamed(GLAccountJsonInputParams.ID.getValue(), element);
final String name = fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.NAME.getValue(), element);
final Long parentId = fromApiJsonHelper.extractLongNamed(GLAccountJsonInputParams.PARENT_ID.getValue(), element);
final String glCode = fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.GL_CODE.getValue(), element);
final Boolean disabled = fromApiJsonHelper.extractBooleanNamed(GLAccountJsonInputParams.DISABLED.getValue(), element);
final Boolean manualEntriesAllowed = fromApiJsonHelper.extractBooleanNamed(
GLAccountJsonInputParams.MANUAL_ENTRIES_ALLOWED.getValue(), element);
final Integer type = fromApiJsonHelper.extractIntegerSansLocaleNamed(GLAccountJsonInputParams.TYPE.getValue(), element);
final Integer usage = fromApiJsonHelper.extractIntegerSansLocaleNamed(GLAccountJsonInputParams.USAGE.getValue(), element);
final String description = fromApiJsonHelper.extractStringNamed(GLAccountJsonInputParams.DESCRIPTION.getValue(), element);
return new GLAccountCommand(id, name, parentId, glCode, disabled, manualEntriesAllowed, type, usage, description);
}
}
@@ -3,12 +3,12 @@
* 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.service;
package org.mifosplatform.accounting.glaccount.service;
import java.util.List;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.glaccount.data.GLAccountData;
import org.mifosplatform.accounting.glaccount.domain.GLAccountType;
public interface GLAccountReadPlatformService {
@@ -3,7 +3,7 @@
* 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.service.impl;
package org.mifosplatform.accounting.glaccount.service;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -11,13 +11,12 @@ import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.api.data.GLAccountData;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.GLAccountUsage;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidClassificationException;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.service.AccountingEnumerations;
import org.mifosplatform.accounting.service.GLAccountReadPlatformService;
import org.mifosplatform.accounting.common.AccountingEnumerations;
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.exception.GLAccountInvalidClassificationException;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
@@ -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,169 @@
/**
* 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.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.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.serialization.GLAccountCommandFromApiJsonDeserializer;
import org.mifosplatform.accounting.journalentry.domain.JournalEntry;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryRepository;
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;
@Autowired
public GLAccountWritePlatformServiceJpaRepositoryImpl(final GLAccountRepository glAccountRepository,
final JournalEntryRepository glJournalEntryRepository, final GLAccountCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.glAccountRepository = glAccountRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@Transactional
@Override
public CommandProcessingResult createGLAccount(final JsonCommand command) {
try {
final GLAccountCommand accountCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
accountCommand.validateForCreate();
// check parent is valid
Long parentId = command.longValueOfParameterNamed(GLAccountJsonInputParams.PARENT_ID.getValue());
GLAccount parentGLAccount = null;
if (parentId != null) {
parentGLAccount = validateParentGLAccount(parentId);
}
GLAccount glAccount = GLAccount.fromJson(parentGLAccount, command);
this.glAccountRepository.saveAndFlush(glAccount);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(glAccount.getId()).build();
} catch (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();
// is the glAccount valid
GLAccount glAccount = 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())) {
Long parentId = command.longValueOfParameterNamed(GLAccountJsonInputParams.PARENT_ID.getValue());
validateParentGLAccount(parentId);
}
/**
* 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()) {
List<JournalEntry> journalEntriesForAccount = 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 (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
List<JournalEntry> journalEntriesForAccount = 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(Long parentAccountId) {
GLAccount parentGLAccount;
parentGLAccount = glAccountRepository.findOne(parentAccountId);
if (parentGLAccount == null) { throw new GLAccountNotFoundException(parentAccountId); }
// ensure parent is not a detail account
if (parentGLAccount.isHeaderAccount()) { throw new GLAccountInvalidParentException(parentAccountId); }
return parentGLAccount;
}
/**
* @param command
* @param dve
*/
private void handleGLAccountDataIntegrityIssues(final JsonCommand command, DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("acc_gl_code")) {
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());
}
}
@@ -3,7 +3,7 @@
* 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.api.infrastructure;
package org.mifosplatform.accounting.journalentry.api;
import java.text.DateFormat;
import java.text.ParseException;
@@ -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.journalentry.api;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.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.JournalEntryData;
import org.mifosplatform.accounting.journalentry.service.JournalEntryReadPlatformService;
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.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<String>(Arrays.asList("id", "officeId", "officeName",
"glAccountName", "glAccountId", "glAccountCode", "glAccountType", "entryDate", "entryType", "amount", "transactionId",
"portfolioGenerated", "entityType", "entityId", "createdByUserId", "createdDate", "createdByUserName", "comments", "reversed"));
private final String resourceNameForPermission = "JOURNALENTRY";
private JournalEntryReadPlatformService journalEntryReadPlatformService;
private final DefaultToApiJsonSerializer<JournalEntryData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public JournalEntriesApiResource(final PlatformSecurityContext context,
final JournalEntryReadPlatformService journalEntryReadPlatformService,
final DefaultToApiJsonSerializer<JournalEntryData> 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 retrieveAllJournalEntries(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId,
@QueryParam("glAccountId") final Long glAccountId, @QueryParam("portfolioGenerated") final Boolean portfolioGenerated,
@QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam,
@QueryParam("transactionId") String transactionId) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
List<JournalEntryData> glJournalEntryDatas = null;
// get dates from date params
Date fromDate = null;
if (fromDateParam != null) {
fromDate = fromDateParam.getDate();
}
Date toDate = null;
if (toDateParam != null) {
toDate = toDateParam.getDate();
}
if (StringUtils.isBlank(transactionId)) {
glJournalEntryDatas = this.journalEntryReadPlatformService.retrieveAllGLJournalEntries(officeId, glAccountId,
portfolioGenerated, fromDate, toDate);
} else {
glJournalEntryDatas = this.journalEntryReadPlatformService.retrieveRelatedJournalEntries(transactionId);
}
final ApiRequestJsonSerializationSettings settings = apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glJournalEntryDatas, 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) {
context.authenticatedUser().validateHasReadPermission(resourceNameForPermission);
final JournalEntryData glJournalEntryData = this.journalEntryReadPlatformService.retrieveGLJournalEntryById(journalEntryId);
final ApiRequestJsonSerializationSettings settings = 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) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createJournalEntry().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@POST
@Path("{transactionId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String createReversalJournalEntry(@PathParam("transactionId") final String transactionId,
@QueryParam("command") final String commandParam) {
CommandProcessingResult result = null;
if (is(commandParam, "reverse")) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().reverseJournalEntry(transactionId).build();
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
} else {
throw new UnrecognizedQueryParamException("command", commandParam);
}
return this.apiJsonSerializerService.serialize(result);
}
private boolean is(final String commandParam, final String commandValue) {
return StringUtils.isNotBlank(commandParam) && commandParam.trim().equalsIgnoreCase(commandValue);
}
}
@@ -0,0 +1,38 @@
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"), ENTRY_DATE("entryDate"), COMMENTS("comments"), CREDITS("credits"), DEBITS("debits"), LOCALE("locale"), DATE_FORMAT(
"dateFormat");
private final String value;
private JournalEntryJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<String>();
static {
for (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;
}
}
@@ -3,23 +3,35 @@
* 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.service;
package org.mifosplatform.accounting.journalentry.command;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.commands.SingleDebitOrCreditEntryCommand;
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.PlatformApiDataValidationException;
public class GLJournalEntryCommandValidator {
/**
* Immutable command for adding an accounting closure
*/
public class JournalEntryCommand {
private final GLJournalEntryCommand command;
private final Long officeId;
private final LocalDate entryDate;
private final String comments;
public GLJournalEntryCommandValidator(GLJournalEntryCommand command) {
this.command = command;
private final SingleDebitOrCreditEntryCommand[] credits;
private final SingleDebitOrCreditEntryCommand[] debits;
public JournalEntryCommand(Long officeId, LocalDate entryDate, String comments, SingleDebitOrCreditEntryCommand[] credits,
SingleDebitOrCreditEntryCommand[] debits) {
this.officeId = officeId;
this.entryDate = entryDate;
this.comments = comments;
this.credits = credits;
this.debits = debits;
}
public void validateForCreate() {
@@ -28,23 +40,23 @@ public class GLJournalEntryCommandValidator {
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLJournalEntry");
baseDataValidator.reset().parameter("entryDate").value(command.getEntryDate()).notBlank();
baseDataValidator.reset().parameter("entryDate").value(this.entryDate).notBlank();
baseDataValidator.reset().parameter("officeId").value(command.getOfficeId()).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("officeId").value(this.officeId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("comments").value(command.getComments()).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("comments").value(this.comments).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("credits").value(command.getCredits()).notNull();
baseDataValidator.reset().parameter("credits").value(this.credits).notNull();
baseDataValidator.reset().parameter("debits").value(command.getDebits()).notNull();
baseDataValidator.reset().parameter("debits").value(this.debits).notNull();
// validation for credit array elements
if (command.getCredits() != null) {
if (command.getCredits().length == 0) {
if (this.credits != null) {
if (this.credits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (SingleDebitOrCreditEntryCommand credit : command.getCredits()) {
for (SingleDebitOrCreditEntryCommand credit : this.credits) {
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
i++;
}
@@ -52,12 +64,12 @@ public class GLJournalEntryCommandValidator {
}
// validation for debit array elements
if (command.getDebits() != null) {
if (command.getDebits().length == 0) {
if (this.debits != null) {
if (this.debits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (SingleDebitOrCreditEntryCommand debit : command.getDebits()) {
for (SingleDebitOrCreditEntryCommand debit : this.debits) {
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
i++;
}
@@ -80,4 +92,25 @@ public class GLJournalEntryCommandValidator {
baseDataValidator.reset().parameter(paramSuffix + "[" + arrayPos + "].amount").value(credit.getAmount()).notNull()
.zeroOrPositiveAmount();
}
public Long getOfficeId() {
return this.officeId;
}
public LocalDate getEntryDate() {
return this.entryDate;
}
public String getComments() {
return this.comments;
}
public SingleDebitOrCreditEntryCommand[] getCredits() {
return this.credits;
}
public SingleDebitOrCreditEntryCommand[] getDebits() {
return this.debits;
}
}
@@ -3,7 +3,7 @@
* 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.api.commands;
package org.mifosplatform.accounting.journalentry.command;
import java.math.BigDecimal;
import java.util.Set;
@@ -3,7 +3,7 @@
* 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.api.data;
package org.mifosplatform.accounting.journalentry.data;
import java.math.BigDecimal;
@@ -16,7 +16,7 @@ import org.mifosplatform.infrastructure.core.data.EnumOptionData;
* Note: no getter/setters required as google will produce json from fields of
* object.
*/
public class GLJournalEntryData {
public class JournalEntryData {
@SuppressWarnings("unused")
private final Long id;
@@ -31,7 +31,7 @@ public class GLJournalEntryData {
@SuppressWarnings("unused")
private final String glAccountCode;
@SuppressWarnings("unused")
private final EnumOptionData glAccountClassification;
private final EnumOptionData glAccountType;
@SuppressWarnings("unused")
private final LocalDate entryDate;
@SuppressWarnings("unused")
@@ -57,7 +57,7 @@ public class GLJournalEntryData {
@SuppressWarnings("unused")
private final Boolean reversed;
public GLJournalEntryData(Long id, Long officeId, String officeName, String glAccountName, Long glAccountId, String glAccountCode,
public JournalEntryData(Long id, Long officeId, String officeName, String glAccountName, Long glAccountId, String glAccountCode,
EnumOptionData glAccountClassification, LocalDate entryDate, EnumOptionData entryType, BigDecimal amount, String transactionId,
Boolean portfolioGenerated, String entityType, Long entityId, Long createdByUserId, LocalDate createdDate,
String createdByUserName, String comments, Boolean reversed) {
@@ -67,7 +67,7 @@ public class GLJournalEntryData {
this.glAccountName = glAccountName;
this.glAccountId = glAccountId;
this.glAccountCode = glAccountCode;
this.glAccountClassification = glAccountClassification;
this.glAccountType = glAccountClassification;
this.entryDate = entryDate;
this.entryType = entryType;
this.amount = amount;
@@ -3,7 +3,7 @@
* 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.api.data;
package org.mifosplatform.accounting.journalentry.data;
/**
* Represents the successful result of an REST API call.
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.journalentry.domain;
import java.math.BigDecimal;
import java.util.Date;
@@ -18,13 +18,14 @@ 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.useradministration.domain.AppUser;
@Entity
@Table(name = "acc_gl_journal_entry")
public class GLJournalEntry extends AbstractAuditableCustom<AppUser, Long> {
public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
@@ -37,7 +38,7 @@ public class GLJournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@SuppressWarnings("unused")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reversal_id")
private GLJournalEntry reversalJournalEntry;
private JournalEntry reversalJournalEntry;
@SuppressWarnings("unused")
@Column(name = "transaction_id", nullable = false, length = 50)
@@ -73,17 +74,17 @@ public class GLJournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@Column(name = "entity_id")
private Long entityId;
public static GLJournalEntry createNew(Office office, GLAccount glAccount, String transactionId, boolean portfolioGenerated,
public static JournalEntry createNew(Office office, GLAccount glAccount, String transactionId, boolean portfolioGenerated,
Date entryDate, JournalEntryType journalEntryType, BigDecimal amount, String description, String entityType, Long entityId) {
return new GLJournalEntry(office, glAccount, transactionId, portfolioGenerated, entryDate, journalEntryType.getValue(), amount,
return new JournalEntry(office, glAccount, transactionId, portfolioGenerated, entryDate, journalEntryType.getValue(), amount,
description, entityType, entityId);
}
protected GLJournalEntry() {
protected JournalEntry() {
//
}
public GLJournalEntry(Office office, GLAccount glAccount, String transactionId, boolean portfolioGenerated, Date entryDate,
public JournalEntry(Office office, GLAccount glAccount, String transactionId, boolean portfolioGenerated, Date entryDate,
Integer type, BigDecimal amount, String description, String entityType, Long entityId) {
this.office = office;
this.glAccount = glAccount;
@@ -123,7 +124,7 @@ public class GLJournalEntry extends AbstractAuditableCustom<AppUser, Long> {
return this.amount;
}
public void setReversalJournalEntry(GLJournalEntry reversalJournalEntry) {
public void setReversalJournalEntry(JournalEntry reversalJournalEntry) {
this.reversalJournalEntry = reversalJournalEntry;
}
@@ -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.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.portfolioGenerated is false")
List<JournalEntry> findUnReversedManualJournalEntriesByTransactionId(@Param("transactionId") String transactionId);
}
@@ -3,11 +3,12 @@
* 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.domain;
package org.mifosplatform.accounting.journalentry.domain;
import java.util.List;
public interface GLJournalEntryRepositoryCustom {
List<GLJournalEntry> findFirstJournalEntryForAccount(long glAccountId);
public interface JournalEntryRepositoryCustom {
List<JournalEntry> findFirstJournalEntryForAccount(long glAccountId);
}
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.journalentry.domain;
import java.util.List;
@@ -13,16 +13,16 @@ import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
@Repository
public class GLJournalEntryRepositoryImpl implements GLJournalEntryRepositoryCustom {
public class JournalEntryRepositoryImpl implements JournalEntryRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
@SuppressWarnings("unchecked")
public List<GLJournalEntry> findFirstJournalEntryForAccount(long glAccountId) {
List<GLJournalEntry> journalEntries = entityManager
.createQuery("SELECT journalEntry FROM GLJournalEntry journalEntry where journalEntry.glAccount.id= :glAccountId")
public List<JournalEntry> findFirstJournalEntryForAccount(long glAccountId) {
List<JournalEntry> journalEntries = entityManager
.createQuery("SELECT journalEntry FROM JournalEntry journalEntry where journalEntry.glAccount.id= :glAccountId")
.setParameter("glAccountId", glAccountId).setFirstResult(0).setMaxResults(1).getResultList();
return journalEntries;
}
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.journalentry.domain;
import java.util.HashMap;
import java.util.Map;
@@ -3,21 +3,21 @@
* 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.exceptions;
package org.mifosplatform.accounting.journalentry.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when journal entry resources are not found.
*/
public class GLJournalEntryNotFoundException extends AbstractPlatformResourceNotFoundException {
public class JournalEntriesNotFoundException extends AbstractPlatformResourceNotFoundException {
public GLJournalEntryNotFoundException(final String transactionId) {
public JournalEntriesNotFoundException(final String transactionId) {
super("error.msg.journalEntries.transactionId.invalid", "Journal Entries with transaction Identifier " + transactionId
+ " does not exist or are not system generated/reversible ", transactionId);
}
public GLJournalEntryNotFoundException(final Long entryId) {
public JournalEntriesNotFoundException(final Long entryId) {
super("error.msg.journalEntries.id.invalid", "Journal Entry with entry Id " + entryId + " does not exist ", entryId);
}
}
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.journalentry.exception;
import java.util.Date;
@@ -12,7 +12,7 @@ import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRul
/**
* A {@link RuntimeException} thrown when a GL Journal Entry is Invalid
*/
public class GLJournalEntryInvalidException extends AbstractPlatformDomainRuleException {
public class JournalEntryInvalidException extends AbstractPlatformDomainRuleException {
/*** enum of reasons for invalid Journal Entry **/
public static enum GL_JOURNAL_ENTRY_INVALID_REASON {
@@ -53,7 +53,7 @@ public class GLJournalEntryInvalidException extends AbstractPlatformDomainRuleEx
}
}
public GLJournalEntryInvalidException(final GL_JOURNAL_ENTRY_INVALID_REASON reason, final Date date, final String accountName,
public JournalEntryInvalidException(final GL_JOURNAL_ENTRY_INVALID_REASON reason, final Date date, final String accountName,
final String accountGLCode) {
super(reason.errorCode(), reason.errorMessage(), date, accountName, accountGLCode);
}
@@ -3,21 +3,21 @@
* 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.exceptions;
package org.mifosplatform.accounting.journalentry.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
* A {@link RuntimeException} thrown when journal entry resources are not found.
*/
public class GLJournalEntriesNotFoundException extends AbstractPlatformResourceNotFoundException {
public class JournalEntryNotFoundException extends AbstractPlatformResourceNotFoundException {
public GLJournalEntriesNotFoundException(final String transactionId) {
public JournalEntryNotFoundException(final String transactionId) {
super("error.msg.journalEntries.transactionId.invalid", "Journal Entries with transaction Identifier " + transactionId
+ " does not exist or are not system generated/reversible ", transactionId);
}
public GLJournalEntriesNotFoundException(final Long entryId) {
public JournalEntryNotFoundException(final Long entryId) {
super("error.msg.journalEntries.id.invalid", "Journal Entry with entry Id " + entryId + " does not exist ", entryId);
}
}
@@ -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.journalentry.handler;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
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
public class CreateJournalEntryCommandHandler implements NewCommandSourceHandler {
private final JournalEntryWritePlatformService writePlatformService;
@Autowired
public CreateJournalEntryCommandHandler(final JournalEntryWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createJournalEntry(command);
}
}
@@ -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.journalentry.handler;
import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService;
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
public class ReverseJournalEntryCommandHandler implements NewCommandSourceHandler {
private final JournalEntryWritePlatformService writePlatformService;
@Autowired
public ReverseJournalEntryCommandHandler(final JournalEntryWritePlatformService writePlatformService) {
this.writePlatformService = writePlatformService;
}
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.revertJournalEntry(command);
}
}
@@ -0,0 +1,102 @@
/**
* 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.serialization;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
/**
* Implementation of {@link FromApiJsonDeserializer} for
* {@link JournalEntryCommand}'s.
*/
@Component
public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {
private final FromJsonHelper fromApiJsonHelper;
@Autowired
public JournalEntryCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper;
}
@Override
public JournalEntryCommand commandFromApiJson(String json) {
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
Set<String> supportedParameters = JournalEntryJsonInputParams.getAllValues();
fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = fromApiJsonHelper.parse(json);
final Long officeId = fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(), element);
final String comments = fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.COMMENTS.getValue(), element);
final LocalDate entryDate = fromApiJsonHelper.extractLocalDateNamed(JournalEntryJsonInputParams.ENTRY_DATE.getValue(), element);
final JsonObject topLevelJsonElement = element.getAsJsonObject();
Locale locale = fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
SingleDebitOrCreditEntryCommand[] credits = null;
SingleDebitOrCreditEntryCommand[] debits = null;
if (element.isJsonObject()) {
if (topLevelJsonElement.has(JournalEntryJsonInputParams.CREDITS.getValue())
&& topLevelJsonElement.get(JournalEntryJsonInputParams.CREDITS.getValue()).isJsonArray()) {
credits = populateCreditsOrDebitsArray(topLevelJsonElement, locale, credits,
JournalEntryJsonInputParams.CREDITS.getValue());
}
if (topLevelJsonElement.has(JournalEntryJsonInputParams.DEBITS.getValue())
&& topLevelJsonElement.get(JournalEntryJsonInputParams.DEBITS.getValue()).isJsonArray()) {
debits = populateCreditsOrDebitsArray(topLevelJsonElement, locale, debits,
JournalEntryJsonInputParams.DEBITS.getValue());
}
}
return new JournalEntryCommand(officeId, entryDate, comments, credits, debits);
}
/**
* @param comments
* @param topLevelJsonElement
* @param locale
*/
private SingleDebitOrCreditEntryCommand[] populateCreditsOrDebitsArray(final JsonObject topLevelJsonElement, final Locale locale,
SingleDebitOrCreditEntryCommand[] debitOrCredits, String paramName) {
final JsonArray array = topLevelJsonElement.get(paramName).getAsJsonArray();
debitOrCredits = new SingleDebitOrCreditEntryCommand[array.size()];
for (int i = 0; i < array.size(); i++) {
final JsonObject creditElement = array.get(i).getAsJsonObject();
final Set<String> parametersPassedInForCreditsCommand = new HashSet<String>();
final Long glAccountId = fromApiJsonHelper.extractLongNamed("glAccountId", creditElement);
final String comments = fromApiJsonHelper.extractStringNamed("comments", creditElement);
final BigDecimal amount = fromApiJsonHelper.extractBigDecimalNamed("amount", creditElement, locale);
debitOrCredits[i] = new SingleDebitOrCreditEntryCommand(parametersPassedInForCreditsCommand, glAccountId, amount, comments);
}
return debitOrCredits;
}
}
@@ -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.journalentry.service;
import java.util.Date;
import java.util.List;
import org.mifosplatform.accounting.journalentry.data.JournalEntryData;
public interface JournalEntryReadPlatformService {
List<JournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate,
Date toDate);
List<JournalEntryData> retrieveRelatedJournalEntries(String transactionId);
JournalEntryData retrieveGLJournalEntryById(long glJournalEntryId);
}
@@ -3,7 +3,7 @@
* 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.service.impl;
package org.mifosplatform.accounting.journalentry.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
@@ -15,10 +15,9 @@ import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.api.data.GLJournalEntryData;
import org.mifosplatform.accounting.exceptions.GLJournalEntriesNotFoundException;
import org.mifosplatform.accounting.service.AccountingEnumerations;
import org.mifosplatform.accounting.service.GLJournalEntryReadPlatformService;
import org.mifosplatform.accounting.common.AccountingEnumerations;
import org.mifosplatform.accounting.journalentry.data.JournalEntryData;
import org.mifosplatform.accounting.journalentry.exception.JournalEntriesNotFoundException;
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
import org.mifosplatform.infrastructure.core.domain.JdbcSupport;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
@@ -29,16 +28,16 @@ import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@Service
public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryReadPlatformService {
public class JournalEntryReadPlatformServiceImpl implements JournalEntryReadPlatformService {
private final JdbcTemplate jdbcTemplate;
@Autowired
public GLJournalEntryReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
public JournalEntryReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
private static final class GLJournalEntryMapper implements RowMapper<GLJournalEntryData> {
private static final class GLJournalEntryMapper implements RowMapper<JournalEntryData> {
public String schema() {
return " journalEntry.id as journalEntryId, glAccount.classification_enum as classification ,"
@@ -55,7 +54,7 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
}
@Override
public GLJournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
public JournalEntryData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
Long journalEntryId = rs.getLong("journalEntryId");
Long officeId = rs.getLong("officeId");
@@ -79,14 +78,14 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
String comments = rs.getString("comments");
Boolean reversed = rs.getBoolean("reversed");
return new GLJournalEntryData(journalEntryId, officeId, officeName, glAccountName, glAccountId, glCode, accountType, entryDate,
return new JournalEntryData(journalEntryId, officeId, officeName, glAccountName, glAccountId, glCode, accountType, entryDate,
entryType, amount, transactionId, portfolioGenerated, entityType, entityId, createdByUserId, createdDate,
createdByUserName, comments, reversed);
}
}
@Override
public List<GLJournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate,
public List<JournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate,
Date toDate) {
GLJournalEntryMapper rm = new GLJournalEntryMapper();
@@ -146,32 +145,32 @@ public class GLJournalEntryReadPlatformServiceImpl implements GLJournalEntryRead
}
@Override
public GLJournalEntryData retrieveGLJournalEntryById(long glJournalEntryId) {
public JournalEntryData retrieveGLJournalEntryById(long glJournalEntryId) {
try {
GLJournalEntryMapper rm = new GLJournalEntryMapper();
String sql = "select " + rm.schema() + " and journalEntry.id = ?";
GLJournalEntryData glJournalEntryData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { glJournalEntryId });
JournalEntryData glJournalEntryData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { glJournalEntryId });
return glJournalEntryData;
} catch (EmptyResultDataAccessException e) {
throw new GLJournalEntriesNotFoundException(glJournalEntryId);
throw new JournalEntriesNotFoundException(glJournalEntryId);
}
}
@Override
public List<GLJournalEntryData> retrieveRelatedJournalEntries(String transactionId) {
public List<JournalEntryData> retrieveRelatedJournalEntries(String transactionId) {
try {
GLJournalEntryMapper rm = new GLJournalEntryMapper();
String sql = "select " + rm.schema() + " and journalEntry.transaction_id = ?";
List<GLJournalEntryData> journalEntryDatas = this.jdbcTemplate.query(sql, rm, new Object[] { transactionId });
List<JournalEntryData> journalEntryDatas = this.jdbcTemplate.query(sql, rm, new Object[] { transactionId });
return journalEntryDatas;
} catch (EmptyResultDataAccessException e) {
throw new GLJournalEntriesNotFoundException(transactionId);
throw new JournalEntriesNotFoundException(transactionId);
}
}
@@ -0,0 +1,24 @@
/**
* 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.service;
import java.util.Map;
import org.mifosplatform.accounting.glaccount.data.LoanDTO;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
public interface JournalEntryWritePlatformService {
CommandProcessingResult createJournalEntry(JsonCommand command);
CommandProcessingResult revertJournalEntry(JsonCommand command);
void createJournalEntriesForLoan(Map<String, Object> accountingBridgeData);
void createJournalEntriesForLoan(LoanDTO loanDTO);
}
@@ -3,7 +3,7 @@
* 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.service.impl;
package org.mifosplatform.accounting.journalentry.service;
import java.math.BigDecimal;
import java.util.ArrayList;
@@ -14,27 +14,30 @@ import java.util.Map;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.time.LocalDate;
import org.mifosplatform.accounting.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.commands.SingleDebitOrCreditEntryCommand;
import org.mifosplatform.accounting.api.data.LoanDTO;
import org.mifosplatform.accounting.api.data.LoanTransactionDTO;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.domain.GLAccountRepository;
import org.mifosplatform.accounting.domain.GLClosure;
import org.mifosplatform.accounting.domain.GLClosureRepository;
import org.mifosplatform.accounting.domain.GLJournalEntry;
import org.mifosplatform.accounting.domain.GLJournalEntryRepository;
import org.mifosplatform.accounting.domain.JournalEntryType;
import org.mifosplatform.accounting.domain.PortfolioProductType;
import org.mifosplatform.accounting.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.exceptions.GLJournalEntriesNotFoundException;
import org.mifosplatform.accounting.exceptions.GLJournalEntryInvalidException;
import org.mifosplatform.accounting.exceptions.GLJournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.mifosplatform.accounting.service.GLJournalEntryCommandValidator;
import org.mifosplatform.accounting.service.GLJournalEntryWritePlatformService;
import org.mifosplatform.accounting.closure.domain.GLClosure;
import org.mifosplatform.accounting.closure.domain.GLClosureRepository;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.glaccount.data.LoanDTO;
import org.mifosplatform.accounting.glaccount.data.LoanTransactionDTO;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand;
import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
import org.mifosplatform.accounting.journalentry.domain.JournalEntry;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryRepository;
import org.mifosplatform.accounting.journalentry.domain.JournalEntryType;
import org.mifosplatform.accounting.journalentry.exception.JournalEntriesNotFoundException;
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException;
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.mifosplatform.accounting.journalentry.serialization.JournalEntryCommandFromApiJsonDeserializer;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
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;
@@ -47,49 +50,56 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJournalEntryWritePlatformService {
public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements JournalEntryWritePlatformService {
private final static Logger logger = LoggerFactory.getLogger(GLJournalEntryWritePlatformServiceJpaRepositoryImpl.class);
private final static Logger logger = LoggerFactory.getLogger(JournalEntryWritePlatformServiceJpaRepositoryImpl.class);
private final GLClosureRepository glClosureRepository;
private final GLAccountRepository glAccountRepository;
private final GLJournalEntryRepository glJournalEntryRepository;
private final JournalEntryRepository glJournalEntryRepository;
private final OfficeRepository officeRepository;
private final ProductToGLAccountMappingRepository accountMappingRepository;
private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer;
@Autowired
public GLJournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final GLAccountRepository glAccountRepository, final GLJournalEntryRepository glJournalEntryRepository,
final OfficeRepository officeRepository, final ProductToGLAccountMappingRepository accountMappingRepository) {
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final GLAccountRepository glAccountRepository, final JournalEntryRepository glJournalEntryRepository,
final OfficeRepository officeRepository, final ProductToGLAccountMappingRepository accountMappingRepository,
final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
this.glAccountRepository = glAccountRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
this.accountMappingRepository = accountMappingRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
}
@Transactional
@Override
public String createJournalEntry(final GLJournalEntryCommand command) {
public CommandProcessingResult createJournalEntry(JsonCommand command) {
try {
GLJournalEntryCommandValidator validator = new GLJournalEntryCommandValidator(command);
validator.validateForCreate();
final JournalEntryCommand journalEntryCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json());
journalEntryCommand.validateForCreate();
// check office is valid
final Office office = this.officeRepository.findOne(command.getOfficeId());
if (office == null) { throw new OfficeNotFoundException(command.getOfficeId()); }
Long officeId = command.longValueOfParameterNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue());
final Office office = this.officeRepository.findOne(officeId);
if (office == null) { throw new OfficeNotFoundException(officeId); }
validateBusinessRulesForJournalEntries(command);
validateBusinessRulesForJournalEntries(journalEntryCommand);
/** Set a transaction Id and save these Journal entries **/
Date entryDate = command.getEntryDate().toDateMidnight().toDate();
Date entryDate = command.DateValueOfParameterNamed(JournalEntryJsonInputParams.ENTRY_DATE.getValue());
String transactionId = generateTransactionId();
saveAllDebitOrCreditEntries(command, office, entryDate, command.getDebits(), transactionId, JournalEntryType.DEBIT);
saveAllDebitOrCreditEntries(journalEntryCommand, office, entryDate, journalEntryCommand.getDebits(), transactionId,
JournalEntryType.DEBIT);
saveAllDebitOrCreditEntries(command, office, entryDate, command.getCredits(), transactionId, JournalEntryType.CREDIT);
saveAllDebitOrCreditEntries(journalEntryCommand, office, entryDate, journalEntryCommand.getCredits(), transactionId,
JournalEntryType.CREDIT);
return transactionId;
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withTransactionId(transactionId).build();
} catch (DataIntegrityViolationException dve) {
handleJournalEntryDataIntegrityIssues(dve);
return null;
@@ -98,27 +108,25 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
@Transactional
@Override
public String revertJournalEntry(final String transactionId) {
public CommandProcessingResult revertJournalEntry(JsonCommand command) {
// is the transaction Id valid
List<GLJournalEntry> journalEntries = glJournalEntryRepository.findUnReversedManualJournalEntriesByTransactionId(transactionId);
List<JournalEntry> journalEntries = glJournalEntryRepository.findUnReversedManualJournalEntriesByTransactionId(command
.getTransactionId());
if (journalEntries.size() <= 1) { throw new GLJournalEntriesNotFoundException(transactionId); }
if (journalEntries.size() <= 1) { throw new JournalEntriesNotFoundException(command.getTransactionId()); }
String reversalTransactionId = generateTransactionId();
for (GLJournalEntry journalEntry : journalEntries) {
GLJournalEntry reversalJournalEntry;
for (JournalEntry journalEntry : journalEntries) {
JournalEntry reversalJournalEntry;
String reversalComment = "Reversal entry for Journal Entry with Entry Id :" + journalEntry.getId() + " and transaction Id "
+ transactionId;
+ command.getTransactionId();
if (journalEntry.isDebitEntry()) {
reversalJournalEntry = GLJournalEntry.createNew(journalEntry.getOffice(), journalEntry.getGlAccount(),
reversalTransactionId, false, journalEntry.getEntryDate(), JournalEntryType.CREDIT, journalEntry.getAmount(),
reversalComment, null, null);
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getGlAccount(), reversalTransactionId,
false, journalEntry.getEntryDate(), JournalEntryType.CREDIT, journalEntry.getAmount(), reversalComment, null, null);
} else {
reversalJournalEntry = GLJournalEntry.createNew(journalEntry.getOffice(), journalEntry.getGlAccount(),
reversalTransactionId, false, journalEntry.getEntryDate(), JournalEntryType.DEBIT, journalEntry.getAmount(),
reversalComment, null, null);
reversalJournalEntry = JournalEntry.createNew(journalEntry.getOffice(), journalEntry.getGlAccount(), reversalTransactionId,
false, journalEntry.getEntryDate(), JournalEntryType.DEBIT, journalEntry.getAmount(), reversalComment, null, null);
}
// save the reversal entry
this.glJournalEntryRepository.saveAndFlush(reversalJournalEntry);
@@ -127,7 +135,7 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
// save the updated journal entry
this.glJournalEntryRepository.saveAndFlush(journalEntry);
}
return reversalTransactionId;
return new CommandProcessingResultBuilder().withTransactionId(reversalTransactionId).build();
}
@Transactional
@@ -206,7 +214,7 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
* the transaction Date
**/
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(entryDate) || latestGLClosure.getClosingDate().equals(entryDate)) { throw new GLJournalEntryInvalidException(
if (latestGLClosure.getClosingDate().after(entryDate) || latestGLClosure.getClosingDate().equals(entryDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
@@ -318,33 +326,32 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
return accountMapping.getGlAccount();
}
private void createCreditJournalEntryForLoanProduct(final Office office, final GLAccount account, final Long loanId,
final String transactionId, final Date entryDate, final BigDecimal amount) {
GLJournalEntry journalEntry = GLJournalEntry.createNew(office, account, transactionId, true, entryDate, JournalEntryType.CREDIT,
private void createCreditJournalEntryForLoanProduct(Office office, GLAccount account, Long loanId, String transactionId,
Date entryDate, BigDecimal amount) {
JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, true, entryDate, JournalEntryType.CREDIT,
amount, null, PortfolioProductType.LOAN.toString(), loanId);
glJournalEntryRepository.saveAndFlush(journalEntry);
}
private void createDebitJournalEntryForLoanProduct(final Office office, final GLAccount account, final Long loanId,
final String transactionId, final Date entryDate, final BigDecimal amount) {
GLJournalEntry journalEntry = GLJournalEntry.createNew(office, account, transactionId, true, entryDate, JournalEntryType.DEBIT,
amount, null, PortfolioProductType.LOAN.toString(), loanId);
private void createDebitJournalEntryForLoanProduct(Office office, GLAccount account, Long loanId, String transactionId, Date entryDate,
BigDecimal amount) {
JournalEntry journalEntry = JournalEntry.createNew(office, account, transactionId, true, entryDate, JournalEntryType.DEBIT, amount,
null, PortfolioProductType.LOAN.toString(), loanId);
glJournalEntryRepository.saveAndFlush(journalEntry);
}
private void validateBusinessRulesForJournalEntries(final GLJournalEntryCommand command) {
private void validateBusinessRulesForJournalEntries(JournalEntryCommand command) {
/** check if date of Journal entry is valid ***/
final LocalDate entryLocalDate = command.getEntryDate();
Date entryDate = entryLocalDate.toDateMidnight().toDate();
// shouldn't be in the future
Date todaysDate = new Date();
if (entryDate.after(todaysDate)) { throw new GLJournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.FUTURE_DATE, entryDate,
if (entryDate.after(todaysDate)) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.FUTURE_DATE, entryDate,
null, null); }
// shouldn't be before an accounting closure
GLClosure latestGLClosure = glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().after(entryDate) || latestGLClosure.getClosingDate().equals(entryDate)) { throw new GLJournalEntryInvalidException(
if (latestGLClosure.getClosingDate().after(entryDate) || latestGLClosure.getClosingDate().equals(entryDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
/*** check if credits and debits are valid **/
@@ -352,27 +359,27 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new GLJournalEntryInvalidException(
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
// sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO;
for (SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new GLJournalEntryInvalidException(
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
}
for (SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new GLJournalEntryInvalidException(
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
}
if (creditsSum.compareTo(debitsSum) != 0) { throw new GLJournalEntryInvalidException(
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
}
private void saveAllDebitOrCreditEntries(final GLJournalEntryCommand command, final Office office, final Date entryDate,
private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final Date entryDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type) {
for (SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) {
@@ -384,9 +391,9 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
* disabled
**/
if (glAccount.isDisabled()) {
throw new GLJournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_DISABLED, null, glAccount.getName(),
throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_DISABLED, null, glAccount.getName(),
glAccount.getGlCode());
} else if (!glAccount.isManualEntriesAllowed()) { throw new GLJournalEntryInvalidException(
} else if (!glAccount.isManualEntriesAllowed()) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED, null, glAccount.getName(),
glAccount.getGlCode()); }
@@ -394,7 +401,7 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
if (!StringUtils.isBlank(singleDebitOrCreditEntryCommand.getComments())) {
comments = singleDebitOrCreditEntryCommand.getComments();
}
GLJournalEntry glJournalEntry = GLJournalEntry.createNew(office, glAccount, transactionId, false, entryDate, type,
JournalEntry glJournalEntry = JournalEntry.createNew(office, glAccount, transactionId, false, entryDate, type,
singleDebitOrCreditEntryCommand.getAmount(), comments, null, null);
glJournalEntryRepository.saveAndFlush(glJournalEntry);
}
@@ -414,4 +421,4 @@ public class GLJournalEntryWritePlatformServiceJpaRepositoryImpl implements GLJo
private String generateTransactionId() {
return RandomStringUtils.random(15, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
}
}
}
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.producttoaccountmapping.domain;
import java.util.HashMap;
import java.util.Map;
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.producttoaccountmapping.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -13,6 +13,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.springframework.data.jpa.domain.AbstractPersistable;
@Entity
@@ -3,7 +3,7 @@
* 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.domain;
package org.mifosplatform.accounting.producttoaccountmapping.domain;
import java.util.List;
@@ -3,7 +3,7 @@
* 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.exceptions;
package org.mifosplatform.accounting.producttoaccountmapping.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
@@ -3,9 +3,9 @@
* 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.exceptions;
package org.mifosplatform.accounting.producttoaccountmapping.exception;
import org.mifosplatform.accounting.domain.PortfolioProductType;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
/**
@@ -3,16 +3,16 @@
* 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.serialization;
package org.mifosplatform.accounting.producttoaccountmapping.serialization;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.glaccount.domain.GLAccount;
import org.mifosplatform.accounting.producttoaccountmapping.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.InvalidJsonException;
@@ -68,8 +68,8 @@ public final class ProductToGLAccountMappingFromApiJsonDeserializer {
final JsonElement element = fromApiJsonHelper.parse(json);
// accounting related data validation
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingType", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingType").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
final Integer accountingRuleType = fromApiJsonHelper.extractIntegerNamed("accountingRule", element, Locale.getDefault());
baseDataValidator.reset().parameter("accountingRule").value(accountingRuleType).notNull().inMinMaxRange(1, 3);
if (isCashBasedAccounting(accountingRuleType) || isAccrualBasedAccounting(accountingRuleType)) {
@@ -3,7 +3,7 @@
* 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.service;
package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.util.Map;
@@ -3,7 +3,7 @@
* 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.service.impl;
package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -11,10 +11,9 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.mifosplatform.accounting.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingReadPlatformService;
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
import org.springframework.beans.factory.annotation.Autowired;
@@ -3,7 +3,7 @@
* 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.service;
package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.util.Map;
@@ -3,27 +3,26 @@
* 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.service.impl;
package org.mifosplatform.accounting.producttoaccountmapping.service;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.mifosplatform.accounting.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.domain.GLAccountRepository;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.PortfolioProductType;
import org.mifosplatform.accounting.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.exceptions.ProductToGLAccountMappingInvalidException;
import org.mifosplatform.accounting.exceptions.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.accounting.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.mifosplatform.accounting.service.ProductToGLAccountMappingWritePlatformService;
import org.mifosplatform.accounting.common.AccountingConstants.ACCRUAL_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.CASH_ACCOUNTS_FOR_LOAN;
import org.mifosplatform.accounting.common.AccountingConstants.LOAN_PRODUCT_ACCOUNTING_PARAMS;
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.GLAccountNotFoundException;
import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.mifosplatform.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingInvalidException;
import org.mifosplatform.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.mifosplatform.accounting.producttoaccountmapping.serialization.ProductToGLAccountMappingFromApiJsonDeserializer;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
import org.mifosplatform.portfolio.loanproduct.domain.AccountingRuleType;
@@ -424,8 +423,8 @@ public class ProductToGLAccountMappingWritePlatformServiceImpl implements Produc
if (glAccount == null) { throw new GLAccountNotFoundException(accountId); }
// validate account is of the expected Type
if (glAccount.getClassification().intValue() != expectedAccountType.getValue()) { throw new ProductToGLAccountMappingInvalidException(
paramName, glAccount.getName(), accountId, GLAccountType.fromInt(glAccount.getClassification()).toString(),
if (glAccount.getType().intValue() != expectedAccountType.getValue()) { throw new ProductToGLAccountMappingInvalidException(
paramName, glAccount.getName(), accountId, GLAccountType.fromInt(glAccount.getType()).toString(),
expectedAccountType.toString()); }
return glAccount;
}
@@ -1,80 +0,0 @@
/**
* 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.service;
import java.util.ArrayList;
import java.util.List;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.mifosplatform.accounting.domain.GLAccountType;
import org.mifosplatform.accounting.domain.GLAccountUsage;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
public class GLAccountCommandValidator {
private final GLAccountCommand command;
public GLAccountCommandValidator(GLAccountCommand command) {
this.command = command;
}
public void validateForCreate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter("name").value(command.getName()).notBlank().notExceedingLengthOf(45);
baseDataValidator.reset().parameter("glCode").value(command.getGlCode()).notBlank().notExceedingLengthOf(45);
baseDataValidator.reset().parameter("parentId").value(command.getParentId()).ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("classification").value(command.getClassification()).notNull()
.inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue());
baseDataValidator.reset().parameter("usage").value(command.getUsage())
.inMinMaxRange(GLAccountUsage.getMinValue(), GLAccountUsage.getMaxValue());
baseDataValidator.reset().parameter("description").value(command.getDescription()).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("manualEntriesAllowed").value(command.getManualEntriesAllowed()).notBlank();
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLAccount");
baseDataValidator.reset().parameter("name").value(command.getName()).ignoreIfNull().notBlank().notExceedingLengthOf(45);
baseDataValidator.reset().parameter("glCode").ignoreIfNull().value(command.getGlCode()).notBlank().notExceedingLengthOf(45);
baseDataValidator.reset().parameter("parentId").value(command.getParentId()).ignoreIfNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("classification").value(command.getClassification()).ignoreIfNull()
.inMinMaxRange(GLAccountType.getMinValue(), GLAccountType.getMaxValue());
baseDataValidator.reset().parameter("usage").value(command.getUsage()).ignoreIfNull()
.inMinMaxRange(GLAccountUsage.getMinValue(), GLAccountUsage.getMaxValue());
baseDataValidator.reset().parameter("description").value(command.getDescription()).ignoreIfNull().notBlank()
.notExceedingLengthOf(500);
baseDataValidator.reset().parameter("disabled").value(command.getDisabled()).ignoreIfNull();
baseDataValidator.reset().anyOfNotNull(command.getName(), command.getGlCode(), command.getParentId(), command.getClassification(),
command.getDescription(), command.getDisabled());
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -1,22 +0,0 @@
/**
* 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.service;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface GLAccountWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_GL_ACCOUNT')")
Long createGLAccount(GLAccountCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'UPDATE_GL_ACCOUNT')")
Long updateGLAccount(Long glAccountId, GLAccountCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_GL_ACCOUNT')")
Long deleteGLAccount(Long glAccountId);
}
@@ -1,22 +0,0 @@
/**
* 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.service;
import org.mifosplatform.accounting.api.commands.GLClosureCommand;
import org.springframework.security.access.prepost.PreAuthorize;
public interface GLClosureWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_GL_CLOSURE')")
Long createGLClosure(GLClosureCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'UPDATE_GL_CLOSURE')")
Long updateGLClosure(Long glClosureId, GLClosureCommand command);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_GL_CLOSURE')")
Long deleteGLClosure(Long glClosureId);
}
@@ -1,22 +0,0 @@
/**
* 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.service;
import java.util.Date;
import java.util.List;
import org.mifosplatform.accounting.api.data.GLJournalEntryData;
public interface GLJournalEntryReadPlatformService {
List<GLJournalEntryData> retrieveAllGLJournalEntries(Long officeId, Long glAccountId, Boolean portfolioGenerated, Date fromDate,
Date toDate);
List<GLJournalEntryData> retrieveRelatedJournalEntries(String transactionId);
GLJournalEntryData retrieveGLJournalEntryById(long glJournalEntryId);
}
@@ -1,35 +0,0 @@
/**
* 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.service;
import java.util.Map;
import org.mifosplatform.accounting.api.commands.GLJournalEntryCommand;
import org.mifosplatform.accounting.api.data.LoanDTO;
import org.springframework.security.access.prepost.PreAuthorize;
public interface GLJournalEntryWritePlatformService {
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_JOURNAL_ENTRY')")
String createJournalEntry(GLJournalEntryCommand journalEntryCommand);
@PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'REVERT_JOURNAL_ENTRY')")
String revertJournalEntry(String transactionId);
void createJournalEntriesForLoan(LoanDTO loanDTO);
/**
* Using this interface over createJournalEntriesForLoan(LoanDTO loanDTO) to
* remove the Object contract and have a 'Data' contract between portfolio
* and accounting subsystems. (portfolio had to know about DTO objects that
* belong in downstream system 'accounting')
*
* If the only thing portfolio knows about how to talk to account is a
* service with a data contract it will be easier move to other means of
* making that communication happen in the future
*/
void createJournalEntriesForLoan(Map<String, Object> accountingBridgeData);
}
@@ -1,155 +0,0 @@
/**
* 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.service.impl;
import java.util.List;
import org.mifosplatform.accounting.api.commands.GLAccountCommand;
import org.mifosplatform.accounting.domain.GLAccount;
import org.mifosplatform.accounting.domain.GLAccountRepository;
import org.mifosplatform.accounting.domain.GLJournalEntry;
import org.mifosplatform.accounting.domain.GLJournalEntryRepository;
import org.mifosplatform.accounting.exceptions.GLAccountDuplicateException;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidDeleteException;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidDeleteException.GL_ACCOUNT_INVALID_DELETE_REASON;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidParentException;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidUpdateException;
import org.mifosplatform.accounting.exceptions.GLAccountInvalidUpdateException.GL_ACCOUNT_INVALID_UPDATE_REASON;
import org.mifosplatform.accounting.exceptions.GLAccountNotFoundException;
import org.mifosplatform.accounting.service.GLAccountCommandValidator;
import org.mifosplatform.accounting.service.GLAccountWritePlatformService;
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 GLJournalEntryRepository glJournalEntryRepository;
@Autowired
public GLAccountWritePlatformServiceJpaRepositoryImpl(final GLAccountRepository glAccountRepository,
final GLJournalEntryRepository glJournalEntryRepository) {
this.glAccountRepository = glAccountRepository;
this.glJournalEntryRepository = glJournalEntryRepository;
}
@Transactional
@Override
public Long createGLAccount(GLAccountCommand command) {
try {
GLAccountCommandValidator validator = new GLAccountCommandValidator(command);
validator.validateForCreate();
// check parent is valid
GLAccount parentGLAccount = null;
if (command.getParentId() != null) {
parentGLAccount = validateParentGLAccount(command);
}
GLAccount glAccount = GLAccount.createNew(parentGLAccount, command.getName(), command.getGlCode(), false,
command.getManualEntriesAllowed(), command.getClassification(), command.getUsage(), command.getDescription());
this.glAccountRepository.saveAndFlush(glAccount);
return glAccount.getId();
} catch (DataIntegrityViolationException dve) {
handleGLAccountDataIntegrityIssues(command, dve);
return Long.valueOf(-1);
}
}
@Transactional
@Override
public Long updateGLAccount(Long glAccountId, GLAccountCommand command) {
GLAccountCommandValidator validator = new GLAccountCommandValidator(command);
validator.validateForUpdate();
// is the glAccount valid
GLAccount glAccount = glAccountRepository.findOne(glAccountId);
if (glAccount == null) { throw new GLAccountNotFoundException(glAccountId); }
// is the new parent valid
GLAccount parentGLAccount = null;
if (command.isParentIdChanged()) {
parentGLAccount = validateParentGLAccount(command);
}
/**
* a detail account cannot be changed to a header account if
* transactions are already logged against it
**/
if (command.isUsageChanged()) {
if (command.isHeaderAccount()) {
List<GLJournalEntry> journalEntriesForAccount = glJournalEntryRepository.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) { throw new GLAccountInvalidUpdateException(
GL_ACCOUNT_INVALID_UPDATE_REASON.TRANSANCTIONS_LOGGED, glAccountId); }
}
}
glAccount.update(command, parentGLAccount);
this.glAccountRepository.saveAndFlush(glAccount);
return glAccount.getId();
}
@Transactional
@Override
public Long deleteGLAccount(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
List<GLJournalEntry> journalEntriesForAccount = glJournalEntryRepository.findFirstJournalEntryForAccount(glAccountId);
if (journalEntriesForAccount.size() > 0) { throw new GLAccountInvalidDeleteException(
GL_ACCOUNT_INVALID_DELETE_REASON.TRANSANCTIONS_LOGGED, glAccountId); }
this.glAccountRepository.delete(glAccount);
return glAccountId;
}
/**
* @param command
* @return
*/
private GLAccount validateParentGLAccount(GLAccountCommand command) {
GLAccount parentGLAccount;
parentGLAccount = glAccountRepository.findOne(command.getParentId());
if (parentGLAccount == null) { throw new GLAccountNotFoundException(command.getParentId()); }
// ensure parent is not a detail account
if (parentGLAccount.isHeaderAccount()) { throw new GLAccountInvalidParentException(command.getParentId()); }
return parentGLAccount;
}
/**
* @param command
* @param dve
*/
private void handleGLAccountDataIntegrityIssues(final GLAccountCommand command, DataIntegrityViolationException dve) {
Throwable realCause = dve.getMostSpecificCause();
if (realCause.getMessage().contains("acc_gl_code")) {
String glCode = command.getGlCode();
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());
}
}

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