From b35d7a62499a75c014b173b244cc556bf4e0a2a7 Mon Sep 17 00:00:00 2001 From: Vishwas Babu AJ Date: Tue, 30 Apr 2013 20:51:27 +0530 Subject: [PATCH] wip:accounting stubs for autoposting and abstractions --- ...ccounting-abstractions-and-autoposting.sql | 47 +++++ .../api/AutoPostingApiResource.java | 139 ++++++++++++ .../api/AutoPostingJsonInputParams.java | 38 ++++ .../command/AutoPostingCommand.java | 64 ++++++ .../autoposting/data/AutoPostingData.java | 113 ++++++++++ .../autoposting/domain/AutoPosting.java | 197 ++++++++++++++++++ .../domain/AutoPostingRepository.java | 17 ++ .../AutoPostingDuplicateException.java | 19 ++ .../AutoPostingInvalidDeleteException.java | 21 ++ .../AutoPostingInvalidException.java | 39 ++++ .../AutoPostingNotFoundException.java | 18 ++ .../CreateAutoPostingCommandHandler.java | 32 +++ .../DeleteAutoPostingCommandHandler.java | 31 +++ .../UpdateAutoPostingCommandHandler.java | 31 +++ ...PostingCommandFromApiJsonDeserializer.java | 59 ++++++ .../AutoPostingReadPlatformService.java | 18 ++ .../AutoPostingReadPlatformServiceImpl.java | 152 ++++++++++++++ .../AutoPostingWritePlatformService.java | 19 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 142 +++++++++++++ .../accounting/closure/domain/GLClosure.java | 4 - .../glaccount/domain/GLAccount.java | 5 - .../domain/GLAccountRepositoryWrapper.java | 33 +++ .../service/GLAccountReadPlatformService.java | 3 +- .../GLAccountReadPlatformServiceImpl.java | 5 + .../journalentry/domain/JournalEntry.java | 2 +- .../domain/PortfolioProductType.java | 8 + .../rule/api/AccountingRuleApiResource.java | 148 +++++++++++++ .../api/AccountingRuleJsonInputParams.java | 38 ++++ .../rule/command/AccountingRuleCommand.java | 79 +++++++ .../rule/data/AccountingRuleData.java | 76 +++++++ .../rule/domain/AccountingRule.java | 150 +++++++++++++ .../rule/domain/AccountingRuleRepository.java | 17 ++ .../AccountingRuleRepositoryWrapper.java | 33 +++ .../AccountingRuleDuplicateException.java | 20 ++ .../AccountingRuleInvalidDeleteException.java | 21 ++ .../AccountingRuleInvalidException.java | 39 ++++ .../AccountingRuleNotFoundException.java | 19 ++ .../CreateAccountingRuleCommandHandler.java | 31 +++ .../DeleteAccountingRuleCommandHandler.java | 31 +++ .../UpdateAccountingRuleCommandHandler.java | 31 +++ ...ingRuleCommandFromApiJsonDeserializer.java | 62 ++++++ .../AccountingRuleReadPlatformService.java | 18 ++ ...AccountingRuleReadPlatformServiceImpl.java | 87 ++++++++ .../AccountingRuleWritePlatformService.java | 19 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 157 ++++++++++++++ .../service/CommandWrapperBuilder.java | 24 +++ .../portfolio/charge/data/ChargeData.java | 16 ++ 47 files changed, 2361 insertions(+), 11 deletions(-) create mode 100755 mifosng-db/migrations/core_db/V28__accounting-abstractions-and-autoposting.sql create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingApiResource.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingJsonInputParams.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/command/AutoPostingCommand.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/data/AutoPostingData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPosting.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPostingRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingDuplicateException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidDeleteException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingNotFoundException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/CreateAutoPostingCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/DeleteAutoPostingCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/UpdateAutoPostingCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/serialization/AutoPostingCommandFromApiJsonDeserializer.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformServiceJpaRepositoryImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccountRepositoryWrapper.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleJsonInputParams.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/command/AccountingRuleCommand.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/data/AccountingRuleData.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRule.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepository.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepositoryWrapper.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleDuplicateException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidDeleteException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleNotFoundException.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/CreateAccountingRuleCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/DeleteAccountingRuleCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/UpdateAccountingRuleCommandHandler.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/serialization/AccountingRuleCommandFromApiJsonDeserializer.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformService.java create mode 100755 mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java diff --git a/mifosng-db/migrations/core_db/V28__accounting-abstractions-and-autoposting.sql b/mifosng-db/migrations/core_db/V28__accounting-abstractions-and-autoposting.sql new file mode 100755 index 000000000..e60a648c4 --- /dev/null +++ b/mifosng-db/migrations/core_db/V28__accounting-abstractions-and-autoposting.sql @@ -0,0 +1,47 @@ +CREATE TABLE `acc_accounting_rule` ( + `id` BIGINT(20) NOT NULL, + `name` VARCHAR(100) NULL DEFAULT NULL, + `office_id` BIGINT(20) NULL DEFAULT NULL, + `debit_account_id` BIGINT(20) NOT NULL, + `credit_account_id` BIGINT(20) NOT NULL, + `description` VARCHAR(500) NULL DEFAULT NULL, + `system_defined` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE INDEX `accounting_rule_name_unique` (`name`), + INDEX `FK_acc_accounting_rule_acc_gl_account_debit` (`debit_account_id`), + INDEX `FK_acc_accounting_rule_acc_gl_account_credit` (`credit_account_id`), + INDEX `FK_acc_accounting_rule_m_office` (`office_id`), + CONSTRAINT `FK_acc_accounting_rule_acc_gl_account_credit` FOREIGN KEY (`credit_account_id`) REFERENCES `acc_gl_account` (`id`), + CONSTRAINT `FK_acc_accounting_rule_acc_gl_account_debit` FOREIGN KEY (`debit_account_id`) REFERENCES `acc_gl_account` (`id`), + CONSTRAINT `FK_acc_accounting_rule_m_office` FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +CREATE TABLE `acc_auto_posting` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `name` VARCHAR(100) NOT NULL, + `description` VARCHAR(500) NULL DEFAULT NULL, + `office_id` BIGINT(20) NULL DEFAULT NULL, + `product_type_enum` SMALLINT(5) NOT NULL, + `product_id` BIGINT(20) NULL DEFAULT NULL, + `charge_id` BIGINT(20) NULL DEFAULT NULL, + `event` INT(11) NOT NULL, + `event_attribute` INT(11) NULL DEFAULT NULL, + `accounting_rule_id` BIGINT(20) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `auto_posting_name_unique` (`name`), + INDEX `FK_acc_auto_posting_m_office` (`office_id`), + INDEX `FK_acc_auto_posting_acc_accounting_rule` (`accounting_rule_id`), + INDEX `FK_acc_auto_posting_m_code` (`event`), + INDEX `FK_acc_auto_posting_m_charge` (`charge_id`), + INDEX `FK_acc_auto_posting_m_code_value` (`event_attribute`), + CONSTRAINT `FK_acc_auto_posting_acc_accounting_rule` FOREIGN KEY (`accounting_rule_id`) REFERENCES `acc_accounting_rule` (`id`), + CONSTRAINT `FK_acc_auto_posting_m_charge` FOREIGN KEY (`charge_id`) REFERENCES `m_charge` (`id`), + CONSTRAINT `FK_acc_auto_posting_m_code` FOREIGN KEY (`event`) REFERENCES `m_code` (`id`), + CONSTRAINT `FK_acc_auto_posting_m_code_value` FOREIGN KEY (`event_attribute`) REFERENCES `m_code_value` (`id`), + CONSTRAINT `FK_acc_auto_posting_m_office` FOREIGN KEY (`office_id`) REFERENCES `m_office` (`id`) +) +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingApiResource.java new file mode 100755 index 000000000..ef5ea49dd --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingApiResource.java @@ -0,0 +1,139 @@ +/** + * 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.autoposting.api; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; + +import org.mifosplatform.accounting.closure.data.GLClosureData; +import org.mifosplatform.accounting.closure.service.GLClosureReadPlatformService; +import org.mifosplatform.commands.domain.CommandWrapper; +import org.mifosplatform.commands.service.CommandWrapperBuilder; +import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; +import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings; +import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer; +import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; +import org.mifosplatform.organisation.office.service.OfficeReadPlatformService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +@Path("/autopostingrules") +@Component +@Scope("singleton") +public class AutoPostingApiResource { + + private static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "officeId", "officeName", + "closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId", "createdByUsername", "lastUpdatedByUserId", + "lastUpdatedByUsername")); + + private final String resourceNameForPermission = "GLCLOSURE"; + + private final GLClosureReadPlatformService glClosureReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final DefaultToApiJsonSerializer apiJsonSerializerService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PlatformSecurityContext context; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public AutoPostingApiResource(final PlatformSecurityContext context, final GLClosureReadPlatformService glClosureReadPlatformService, + final DefaultToApiJsonSerializer toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final OfficeReadPlatformService officeReadPlatformService) { + this.context = context; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiJsonSerializerService = toApiJsonSerializer; + this.glClosureReadPlatformService = glClosureReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAllClosures(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); + final List glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.apiJsonSerializerService.serialize(settings, glClosureDatas, RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{glClosureId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retreiveClosure(@PathParam("glClosureId") final Long glClosureId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId); + if (settings.isTemplate()) { + // glClosureData.setAllowedOffices(new + // ArrayList(this.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); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingJsonInputParams.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingJsonInputParams.java new file mode 100755 index 000000000..73e2cb5d4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/api/AutoPostingJsonInputParams.java @@ -0,0 +1,38 @@ +package org.mifosplatform.accounting.autoposting.api; + +import java.util.HashSet; +import java.util.Set; + +/*** + * Enum of all parameters passed in while creating/updating a loan product + ***/ +public enum AutoPostingJsonInputParams { + ID("id"), OFFICE_ID("officeId"), NAME("name"), DESCRIPTION("description"), PRODUCT_TYPE_ENUM("productTypeEnum"), PRODUCT_ID("productId"), CHARGE_ID( + "chargeId"), EVENT_ID("eventCodeId"), EVENT_ATTRIBUTE_ID("eventAttributeId"), ACCOUNTING_RULE_ID("accountingRuleId"); + + private final String value; + + private AutoPostingJsonInputParams(final String value) { + this.value = value; + } + + private static final Set values = new HashSet(); + static { + for (final AutoPostingJsonInputParams type : AutoPostingJsonInputParams.values()) { + values.add(type.value); + } + } + + public static Set getAllValues() { + return values; + } + + @Override + public String toString() { + return name().toString().replaceAll("_", " "); + } + + public String getValue() { + return this.value; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/command/AutoPostingCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/command/AutoPostingCommand.java new file mode 100755 index 000000000..20841797b --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/command/AutoPostingCommand.java @@ -0,0 +1,64 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.command; + +import java.util.ArrayList; +import java.util.List; + +import org.joda.time.LocalDate; +import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +/** + * Immutable command for adding an accounting closure + */ +public class AutoPostingCommand { + + @SuppressWarnings("unused") + private final Long id; + private final Long officeId; + private final LocalDate closingDate; + private final String comments; + + public AutoPostingCommand(final Long id, final Long officeId, final LocalDate closingDate, final String comments) { + this.id = id; + this.officeId = officeId; + this.closingDate = closingDate; + this.comments = comments; + } + + public void validateForCreate() { + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure"); + + baseDataValidator.reset().parameter(GLClosureJsonInputParams.CLOSING_DATE.getValue()).value(this.closingDate).notBlank(); + baseDataValidator.reset().parameter(GLClosureJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).notNull() + .integerGreaterThanZero(); + baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull() + .notExceedingLengthOf(500); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + + public void validateForUpdate() { + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure"); + + baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull() + .notExceedingLengthOf(500); + baseDataValidator.reset().anyOfNotNull(this.comments); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/data/AutoPostingData.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/data/AutoPostingData.java new file mode 100755 index 000000000..5feb2b5f4 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/data/AutoPostingData.java @@ -0,0 +1,113 @@ +/** + * 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.autoposting.data; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.accounting.rule.data.AccountingRuleData; +import org.mifosplatform.infrastructure.codes.data.CodeData; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; +import org.mifosplatform.portfolio.savings.data.SavingsProductData; + +/** + * Immutable object representing a Auto posting rule + * + * Note: no getter/setters required as google-gson will produce json from fields + * of object. + */ +public class AutoPostingData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final String name; + @SuppressWarnings("unused") + private String description; + @SuppressWarnings("unused") + private OfficeData officeData; + @SuppressWarnings("unused") + private EnumOptionData productType; + @SuppressWarnings("unused") + private LoanProductData loanProductData; + @SuppressWarnings("unused") + private SavingsProductData savingProductData; + @SuppressWarnings("unused") + private ChargeData charge; + @SuppressWarnings("unused") + private CodeData event; + @SuppressWarnings("unused") + private CodeValueData eventAttribute; + @SuppressWarnings("unused") + private AccountingRuleData accountingRule; + + // template + @SuppressWarnings("unused") + private List allowedProductTypes = new ArrayList(); + @SuppressWarnings("unused") + private List allowedOffices = new ArrayList(); + @SuppressWarnings("unused") + private List allowedSavingProducts = new ArrayList(); + @SuppressWarnings("unused") + private List allowedLoanProducts = new ArrayList(); + @SuppressWarnings("unused") + private List allowedEventAttributes = new ArrayList(); + + public AutoPostingData(Long id, String name, String description, OfficeData officeData, EnumOptionData productType, + LoanProductData loanProductData, SavingsProductData savingsProductData, ChargeData charge, CodeData event, + CodeValueData eventAttribute, AccountingRuleData accountingRule) { + this(id, name, description, officeData, productType, loanProductData, savingsProductData, charge, event, eventAttribute, + accountingRule, null, null, null, null, null); + } + + private AutoPostingData(Long id, String name, String description, OfficeData officeData, EnumOptionData productType, + LoanProductData loanProductData, SavingsProductData savingsProductData, ChargeData charge, CodeData event, + CodeValueData eventAttribute, AccountingRuleData accountingRule, List allowedProductTypes, + List allowedOffices, List allowedSavingProducts, List allowedLoanProducts, + List allowedEventAttributes) { + this.id = id; + this.name = name; + this.description = description; + this.officeData = officeData; + this.productType = productType; + this.loanProductData = loanProductData; + this.savingProductData = savingsProductData; + this.charge = charge; + this.event = event; + this.eventAttribute = eventAttribute; + this.accountingRule = accountingRule; + this.allowedProductTypes = allowedProductTypes; + this.allowedOffices = allowedOffices; + this.allowedSavingProducts = allowedSavingProducts; + this.allowedLoanProducts = allowedLoanProducts; + this.allowedEventAttributes = allowedEventAttributes; + } + + public void setAllowedProductTypes(List allowedProductTypes) { + this.allowedProductTypes = allowedProductTypes; + } + + public void setAllowedOffices(List allowedOffices) { + this.allowedOffices = allowedOffices; + } + + public void setAllowedSavingProducts(List allowedSavingProducts) { + this.allowedSavingProducts = allowedSavingProducts; + } + + public void setAllowedLoanProducts(List allowedLoanProducts) { + this.allowedLoanProducts = allowedLoanProducts; + } + + public void setAllowedEventAttributes(List allowedEventAttributes) { + this.allowedEventAttributes = allowedEventAttributes; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPosting.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPosting.java new file mode 100755 index 000000000..5492fde22 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPosting.java @@ -0,0 +1,197 @@ +/** + * 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.autoposting.domain; + +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.UniqueConstraint; + +import org.mifosplatform.accounting.autoposting.api.AutoPostingJsonInputParams; +import org.mifosplatform.accounting.rule.domain.AccountingRule; +import org.mifosplatform.infrastructure.codes.domain.Code; +import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.portfolio.charge.domain.Charge; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "acc_auto_posting", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "auto_posting_name_unique") }) +public class AutoPosting extends AbstractPersistable { + + @Column(name = "name", nullable = true, length = 100) + private String name; + + @Column(name = "description", nullable = true, length = 500) + private String description; + + @ManyToOne + @JoinColumn(name = "office_id", nullable = true) + private Office office; + + @Column(name = "type_enum", nullable = false) + private Integer productTypeEnum; + + @Column(name = "product_id", nullable = true) + private Long productId; + + @ManyToOne + @JoinColumn(name = "charge_id", nullable = true) + private Charge charge; + + @ManyToOne + @JoinColumn(name = "event", nullable = false) + private Code event; + + @ManyToOne + @JoinColumn(name = "event_attribute", nullable = true) + private CodeValue eventAttribute; + + @ManyToOne + @JoinColumn(name = "accounting_rule_id", nullable = true) + private AccountingRule accountingRule; + + protected AutoPosting() { + // + } + + private AutoPosting(Office office, Charge charge, Code event, CodeValue eventAttribute, AccountingRule accountingRule, String name, + String description, Integer productTypeEnum, Long productId) { + this.name = name; + this.description = description; + this.office = office; + this.productTypeEnum = productTypeEnum; + this.productId = productId; + this.charge = charge; + this.event = event; + this.eventAttribute = eventAttribute; + this.accountingRule = accountingRule; + } + + public static AutoPosting fromJson(Office office, Charge charge, Code event, CodeValue eventAttribute, AccountingRule accountingRule, + JsonCommand command) { + final String name = command.stringValueOfParameterNamed(AutoPostingJsonInputParams.NAME.getValue()); + final String description = command.stringValueOfParameterNamed(AutoPostingJsonInputParams.DESCRIPTION.getValue()); + final Integer productTypeEnum = command.integerValueSansLocaleOfParameterNamed(AutoPostingJsonInputParams.PRODUCT_TYPE_ENUM + .getValue()); + final Long productId = command.longValueOfParameterNamed(AutoPostingJsonInputParams.PRODUCT_ID.getValue()); + return new AutoPosting(office, charge, event, eventAttribute, accountingRule, name, description, productTypeEnum, productId); + } + + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(5); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.OFFICE_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.CHARGE_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.EVENT_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.EVENT_ATTRIBUTE_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.ACCOUNTING_RULE_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.NAME.getValue(), this.name); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.DESCRIPTION.getValue(), this.description); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.PRODUCT_TYPE_ENUM.getValue(), this.productTypeEnum, true); + handlePropertyUpdate(command, actualChanges, AutoPostingJsonInputParams.PRODUCT_ID.getValue(), this.productId); + return actualChanges; + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + final String propertyToBeUpdated) { + if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) { + final String newValue = command.stringValueOfParameterNamed(paramName); + actualChanges.put(paramName, newValue); + // now update actual property + if (paramName.equals(AutoPostingJsonInputParams.DESCRIPTION.getValue())) { + this.description = newValue; + } else if (paramName.equals(AutoPostingJsonInputParams.NAME.getValue())) { + this.name = newValue; + } + } + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + Integer propertyToBeUpdated, boolean sansLocale) { + if (command.isChangeInIntegerParameterNamed(paramName, propertyToBeUpdated)) { + 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(AutoPostingJsonInputParams.PRODUCT_TYPE_ENUM.getValue())) { + this.productTypeEnum = newValue; + } + } + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + final Long propertyToBeUpdated) { + if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) { + final Long newValue = command.longValueOfParameterNamed(paramName); + actualChanges.put(paramName, newValue); + // now update actual property + if (paramName.equals(AutoPostingJsonInputParams.OFFICE_ID.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AutoPostingJsonInputParams.CHARGE_ID.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AutoPostingJsonInputParams.EVENT_ID.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AutoPostingJsonInputParams.EVENT_ATTRIBUTE_ID.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AutoPostingJsonInputParams.ACCOUNTING_RULE_ID.getValue())) { + // do nothing as this is a nested property + } + } + } + + public Office getOffice() { + return this.office; + } + + public void setOffice(Office office) { + this.office = office; + } + + public Charge getCharge() { + return this.charge; + } + + public void setCharge(Charge charge) { + this.charge = charge; + } + + public Code getEvent() { + return this.event; + } + + public void setEvent(Code event) { + this.event = event; + } + + public CodeValue getEventAttribute() { + return this.eventAttribute; + } + + public void setEventAttribute(CodeValue eventAttribute) { + this.eventAttribute = eventAttribute; + } + + public AccountingRule getAccountingRule() { + return this.accountingRule; + } + + public void setAccountingRule(AccountingRule accountingRule) { + this.accountingRule = accountingRule; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPostingRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPostingRepository.java new file mode 100755 index 000000000..aeec7f885 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/domain/AutoPostingRepository.java @@ -0,0 +1,17 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface AutoPostingRepository extends JpaRepository, JpaSpecificationExecutor { + + @Query("from GLClosure closure where closure.closingDate = (select max(closure1.closingDate) from GLClosure closure1 where closure1.office.id=:officeId) and closure.office.id= :officeId") + AutoPosting getLatestGLClosureByBranch(@Param("officeId") Long officeId); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingDuplicateException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingDuplicateException.java new file mode 100755 index 000000000..6f3217ddc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingDuplicateException.java @@ -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.autoposting.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when a Auto posting rule is already present + */ +public class AutoPostingDuplicateException extends AbstractPlatformDomainRuleException { + + public AutoPostingDuplicateException(final String autoPostingName) { + super("error.msg.auto.posting.duplicate", "An autoposting rule with the name " + autoPostingName + " already exists", + autoPostingName); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidDeleteException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidDeleteException.java new file mode 100755 index 000000000..331547979 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidDeleteException.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.exception; + +import java.util.Date; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when a GL Closure Delte command is invalid + */ +public class AutoPostingInvalidDeleteException extends AbstractPlatformDomainRuleException { + + public AutoPostingInvalidDeleteException(final Long officeId, final String officeName, final Date latestclosureDate) { + super("error.msg.glclosure.invalid.delete", "The latest closure for office with Id " + officeId + " and name " + officeName + + " is on " + latestclosureDate.toString() + ", please delete this closure first", latestclosureDate); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidException.java new file mode 100755 index 000000000..949df1829 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingInvalidException.java @@ -0,0 +1,39 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.exception; + +import java.util.Date; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when a GL Closure is Invalid + */ +public class AutoPostingInvalidException extends AbstractPlatformDomainRuleException { + + /*** enum of reasons for invalid Accounting Closure **/ + public static enum GL_CLOSURE_INVALID_REASON { + FUTURE_DATE, ACCOUNTING_CLOSED; + + public String errorMessage() { + if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { + return "Accounting closures cannot be made for a future date"; + } else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "Accounting Closure for this branch has already been defined for a greater date"; } + return name().toString(); + } + + public String errorCode() { + if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { + return "error.msg.glclosure.invalid.future.date"; + } else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "error.msg.glclosure.invalid.accounting.closed"; } + return name().toString(); + } + } + + public AutoPostingInvalidException(final GL_CLOSURE_INVALID_REASON reason, final Date date) { + super(reason.errorCode(), reason.errorMessage(), date); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingNotFoundException.java new file mode 100755 index 000000000..4d724c369 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/exception/AutoPostingNotFoundException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when Auto posting rule is not found. + */ +public class AutoPostingNotFoundException extends AbstractPlatformResourceNotFoundException { + + public AutoPostingNotFoundException(final Long id) { + super("error.msg.autoposting.id.invalid", "Autoposting rule with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/CreateAutoPostingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/CreateAutoPostingCommandHandler.java new file mode 100755 index 000000000..964d906b7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/CreateAutoPostingCommandHandler.java @@ -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.autoposting.handler; + +import org.mifosplatform.accounting.autoposting.service.AutoPostingWritePlatformService; +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 CreateAutoPostingCommandHandler implements NewCommandSourceHandler { + + private final AutoPostingWritePlatformService writePlatformService; + + @Autowired + public CreateAutoPostingCommandHandler(final AutoPostingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + + return this.writePlatformService.createAutoPostingRule(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/DeleteAutoPostingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/DeleteAutoPostingCommandHandler.java new file mode 100755 index 000000000..54bba6f8c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/DeleteAutoPostingCommandHandler.java @@ -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.autoposting.handler; + +import org.mifosplatform.accounting.autoposting.service.AutoPostingWritePlatformService; +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 DeleteAutoPostingCommandHandler implements NewCommandSourceHandler { + + private final AutoPostingWritePlatformService writePlatformService; + + @Autowired + public DeleteAutoPostingCommandHandler(final AutoPostingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteAutoPostingRule(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/UpdateAutoPostingCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/UpdateAutoPostingCommandHandler.java new file mode 100755 index 000000000..8f7218542 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/handler/UpdateAutoPostingCommandHandler.java @@ -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.autoposting.handler; + +import org.mifosplatform.accounting.autoposting.service.AutoPostingWritePlatformService; +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 UpdateAutoPostingCommandHandler implements NewCommandSourceHandler { + + private final AutoPostingWritePlatformService writePlatformService; + + @Autowired + public UpdateAutoPostingCommandHandler(final AutoPostingWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.updateAutoPostingRule(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/serialization/AutoPostingCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/serialization/AutoPostingCommandFromApiJsonDeserializer.java new file mode 100755 index 000000000..c8d6fabcc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/serialization/AutoPostingCommandFromApiJsonDeserializer.java @@ -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.autoposting.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 AutoPostingCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AutoPostingCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper; + } + + @Override + public GLClosureCommand commandFromApiJson(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + final Set supportedParameters = GLClosureJsonInputParams.getAllValues(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final Long id = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.ID.getValue(), element); + final Long officeId = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.OFFICE_ID.getValue(), element); + final String comments = this.fromApiJsonHelper.extractStringNamed(GLClosureJsonInputParams.COMMENTS.getValue(), element); + final LocalDate closingDate = this.fromApiJsonHelper.extractLocalDateNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue(), + element); + + return new GLClosureCommand(id, officeId, closingDate, comments); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformService.java new file mode 100755 index 000000000..708413363 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.autoposting.service; + +import java.util.List; + +import org.mifosplatform.accounting.autoposting.data.AutoPostingData; + +public interface AutoPostingReadPlatformService { + + List retrieveAllAutoPostingRules(); + + AutoPostingData retrieveAutoPostingRuleById(long autopostingId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformServiceImpl.java new file mode 100755 index 000000000..3e9327a2f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingReadPlatformServiceImpl.java @@ -0,0 +1,152 @@ +/** + * 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.autoposting.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import org.mifosplatform.accounting.autoposting.data.AutoPostingData; +import org.mifosplatform.accounting.closure.exception.GLClosureNotFoundException; +import org.mifosplatform.accounting.common.AccountingEnumerations; +import org.mifosplatform.accounting.producttoaccountmapping.domain.PortfolioProductType; +import org.mifosplatform.accounting.rule.data.AccountingRuleData; +import org.mifosplatform.infrastructure.codes.data.CodeData; +import org.mifosplatform.infrastructure.codes.data.CodeValueData; +import org.mifosplatform.infrastructure.core.data.EnumOptionData; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; +import org.mifosplatform.organisation.office.data.OfficeData; +import org.mifosplatform.portfolio.charge.data.ChargeData; +import org.mifosplatform.portfolio.loanproduct.data.LoanProductData; +import org.mifosplatform.portfolio.savings.data.SavingsProductData; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class AutoPostingReadPlatformServiceImpl implements AutoPostingReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public AutoPostingReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class AutoPostingMapper implements RowMapper { + + private final String schemaSql; + + public AutoPostingMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder + .append("select autoposting.id as id, autoposting.name as name, autoposting.description as description,") + .append(" autoposting.office_id as officeId, office.name as officeName, autoposting.product_type_enum as productTypeEnum,") + .append(" autoposting.product_id as productId, autoposting.charge_id as chargeId, autoposting.event as event,") + .append(" autoposting.event_attribute as eventAttribute, autoposting.accounting_rule_id as accountingRuleId,") + .append(" savingsproduct.name as savingProductName, loanproduct.name as loanProductName,") + .append(" charge.name as chargeName, charge.is_penalty as isPenalty,") + .append(" event.code_name as eventName, eventAttribute.code_value as eventAttributeName") + .append(" accountingRule.debit_account_id as debitAccountId, accountingRule.credit_account_id as creditAccountId") + .append(" from acc_auto_posting autoposting left join m_office office on autoposting.office_id=office.id") + .append(" left join m_savings_product savingsproduct on autoposting.product_id=savingsproduct.id") + .append(" left join m_product_loan loanproduct on autoposting.product_id=loanproduct.id") + .append(" left join m_charge charge on autoposting.charge_id=charge.id") + .append(" left join m_code event on autoposting.event=event.id") + .append(" left join m_code_value eventAttribute on autoposting.event_attribute=eventAttribute.id") + .append(" left join acc_accounting_rule accountingRule on autoposting.accounting_rule_id= accountingRule.id"); + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public AutoPostingData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final String name = rs.getString("name"); + final String description = rs.getString("description"); + final Long officeId = JdbcSupport.getLong(rs, "officeId"); + final String officeName = rs.getString("officeName"); + final int productTypeEnum = rs.getInt("productTypeEnum"); + final Long productId = JdbcSupport.getLong(rs, "productId"); + final Long chargeId = JdbcSupport.getLong(rs, "chargeId"); + final Long accountingRuleId = JdbcSupport.getLong(rs, "accountingRuleId"); + final Long eventId = JdbcSupport.getLong(rs, "eventId"); + final Long eventAttributeId = JdbcSupport.getLong(rs, "eventAttributeId"); + final Long debitAccountId = JdbcSupport.getLong(rs, "debitAccountId"); + final Long creditAccountId = JdbcSupport.getLong(rs, "creditAccountId"); + final String savingProductName = rs.getString("savingProductName"); + final String loanProductName = rs.getString("loanProductName"); + final String chargeName = rs.getString("chargeName"); + final String eventName = rs.getString("eventName"); + final String eventAttributeName = rs.getString("eventAttributeName"); + final boolean chargeIsPenalty = rs.getBoolean("isPenalty"); + + OfficeData officeData = null; + if (officeId != null) { + officeData = OfficeData.dropdown(officeId, officeName, null); + } + + SavingsProductData savingsProductData = null; + LoanProductData loanProductData = null; + + PortfolioProductType portfolioProductType = PortfolioProductType.fromInt(productTypeEnum); + EnumOptionData productType = AccountingEnumerations.portfolioProductType(portfolioProductType); + if (portfolioProductType.isLoanProduct() && productId != null) { + savingsProductData = SavingsProductData.lookup(productId, savingProductName); + } else if (portfolioProductType.isSavingProduct() && productId != null) { + loanProductData = LoanProductData.lookup(productId, loanProductName); + } + + ChargeData chargeData = null; + if (chargeId != null) { + chargeData = ChargeData.lookup(chargeId, chargeName, chargeIsPenalty); + } + + CodeData event = CodeData.instance(eventId, eventName, true); + + CodeValueData eventAttribute = null; + if (eventAttributeId != null) { + eventAttribute = CodeValueData.instance(eventAttributeId, eventAttributeName); + } + + AccountingRuleData accountingRuleData = new AccountingRuleData(accountingRuleId, debitAccountId, creditAccountId); + + return new AutoPostingData(id, name, description, officeData, productType, loanProductData, savingsProductData, chargeData, + event, eventAttribute, accountingRuleData); + } + } + + @Override + public List retrieveAllAutoPostingRules() { + final AutoPostingMapper rm = new AutoPostingMapper(); + + String sql = "select " + rm.schema(); + return this.jdbcTemplate.query(sql, rm, new Object[] {}); + } + + @Override + public AutoPostingData retrieveAutoPostingRuleById(final long autopostingId) { + try { + + final AutoPostingMapper rm = new AutoPostingMapper(); + final String sql = "select " + rm.schema() + " and glClosure.id = ?"; + + final AutoPostingData autoPostingData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { autopostingId }); + + return autoPostingData; + } catch (final EmptyResultDataAccessException e) { + throw new GLClosureNotFoundException(autopostingId); + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformService.java new file mode 100755 index 000000000..7f29074cf --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformService.java @@ -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.autoposting.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface AutoPostingWritePlatformService { + + CommandProcessingResult createAutoPostingRule(JsonCommand command); + + CommandProcessingResult updateAutoPostingRule(Long autoPostingId, JsonCommand command); + + CommandProcessingResult deleteAutoPostingRule(Long autoPostingId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformServiceJpaRepositoryImpl.java new file mode 100755 index 000000000..8b3efa3bc --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/autoposting/service/AutoPostingWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,142 @@ +/** + * 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.autoposting.service; + +import java.util.Date; +import java.util.Map; + +import org.mifosplatform.accounting.autoposting.api.AutoPostingJsonInputParams; +import org.mifosplatform.accounting.autoposting.exception.AutoPostingDuplicateException; +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.GLClosureInvalidDeleteException; +import org.mifosplatform.accounting.closure.exception.GLClosureInvalidException; +import org.mifosplatform.accounting.closure.exception.GLClosureInvalidException.GL_CLOSURE_INVALID_REASON; +import org.mifosplatform.accounting.closure.exception.GLClosureNotFoundException; +import org.mifosplatform.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.office.domain.OfficeRepository; +import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AutoPostingWritePlatformServiceJpaRepositoryImpl implements AutoPostingWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(AutoPostingWritePlatformServiceJpaRepositoryImpl.class); + + private final GLClosureRepository glClosureRepository; + private final OfficeRepository officeRepository; + private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer; + + @Autowired + public AutoPostingWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository, + final OfficeRepository officeRepository, final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer) { + this.glClosureRepository = glClosureRepository; + this.officeRepository = officeRepository; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; + } + + @Transactional + @Override + public CommandProcessingResult createAutoPostingRule(final JsonCommand command) { + try { + final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); + closureCommand.validateForCreate(); + + // check office is valid + final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue()); + final Office office = this.officeRepository.findOne(officeId); + if (office == null) { throw new OfficeNotFoundException(officeId); } + + // TODO: Get Tenant specific date + // ensure closure date is not in the future + final Date todaysDate = new Date(); + final Date closureDate = command.DateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue()); + if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(GL_CLOSURE_INVALID_REASON.FUTURE_DATE, closureDate); } + // shouldn't be before an existing accounting closure + final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(officeId); + if (latestGLClosure != null) { + if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException( + GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); } + } + final GLClosure glClosure = GLClosure.fromJson(office, command); + + this.glClosureRepository.saveAndFlush(glClosure); + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId) + .withEntityId(glClosure.getId()).build(); + } catch (final DataIntegrityViolationException dve) { + handleAutoPostingIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult updateAutoPostingRule(final Long autoPostingId, final JsonCommand command) { + final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); + closureCommand.validateForUpdate(); + + // is the glClosure valid + final GLClosure glClosure = this.glClosureRepository.findOne(autoPostingId); + if (glClosure == null) { throw new GLClosureNotFoundException(autoPostingId); } + + final Map changesOnly = glClosure.update(command); + + if (!changesOnly.isEmpty()) { + this.glClosureRepository.saveAndFlush(glClosure); + } + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(glClosure.getOffice().getId()) + .withEntityId(glClosure.getId()).with(changesOnly).build(); + } + + @Transactional + @Override + public CommandProcessingResult deleteAutoPostingRule(final Long autoPostingId) { + final GLClosure glClosure = this.glClosureRepository.findOne(autoPostingId); + + if (glClosure == null) { throw new GLClosureNotFoundException(autoPostingId); } + + /** + * check if any closures are present for this branch at a later date + * than this closure date + **/ + final Date closureDate = glClosure.getClosingDate(); + final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(glClosure.getOffice().getId()); + if (latestGLClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidDeleteException(latestGLClosure.getOffice() + .getId(), latestGLClosure.getOffice().getName(), latestGLClosure.getClosingDate()); } + + this.glClosureRepository.delete(glClosure); + + return new CommandProcessingResultBuilder().withOfficeId(glClosure.getOffice().getId()).withEntityId(glClosure.getId()).build(); + } + + /** + * @param command + * @param dve + */ + private void handleAutoPostingIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("auto_posting_name_unique")) { throw new AutoPostingDuplicateException( + command.stringValueOfParameterNamed(AutoPostingJsonInputParams.NAME.getValue())); } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue", + "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage()); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java index 132d480c3..5d57e6024 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/closure/domain/GLClosure.java @@ -44,10 +44,6 @@ public class GLClosure extends AbstractAuditableCustom { @Column(name = "comments", nullable = true, length = 500) private String comments; - public static GLClosure createNew(final Office office, final Date closingDate, final String comments) { - return new GLClosure(office, closingDate, comments); - } - protected GLClosure() { // } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccount.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccount.java index de667ad1f..812692b15 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccount.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccount.java @@ -59,11 +59,6 @@ public class GLAccount extends AbstractPersistable { @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() { // } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccountRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccountRepositoryWrapper.java new file mode 100755 index 000000000..e5daf2070 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/domain/GLAccountRepositoryWrapper.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.glaccount.domain; + +import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link GLAccountRepository} . + *

+ */ +@Service +public class GLAccountRepositoryWrapper { + + private final GLAccountRepository repository; + + @Autowired + public GLAccountRepositoryWrapper(final GLAccountRepository repository) { + this.repository = repository; + } + + public GLAccount findOneWithNotFoundDetection(final Long id) { + final GLAccount account = this.repository.findOne(id); + if (account == null) { throw new GLAccountNotFoundException(id); } + return account; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java index 526f34700..5f51428f6 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java @@ -17,8 +17,9 @@ public interface GLAccountReadPlatformService { GLAccountData retrieveGLAccountById(long glAccountId); + List retrieveAllEnabledDetailGLAccounts(); + List retrieveAllEnabledDetailGLAccounts(GLAccountType accountType); GLAccountData retrieveNewGLAccountDetails(); - } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java index 595bdb22b..52ea07020 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java @@ -166,6 +166,11 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false); } + @Override + public List retrieveAllEnabledDetailGLAccounts() { + return retrieveAllGLAccounts(null, null, GLAccountUsage.DETAIL.getValue(), null, false); + } + private static boolean checkValidGLAccountType(final int type) { for (final GLAccountType accountType : GLAccountType.values()) { if (accountType.getValue().equals(type)) { return true; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java index 6a9227754..df907d492 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/domain/JournalEntry.java @@ -56,7 +56,7 @@ public class JournalEntry extends AbstractAuditableCustom { @Temporal(TemporalType.DATE) private Date transactionDate; - @Column(name = "type_enum", nullable = false, length = 50) + @Column(name = "type_enum", nullable = false) private Integer type; @Column(name = "amount", scale = 6, precision = 19, nullable = false) diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java index 7c8f10e58..3bb1a6a8e 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/producttoaccountmapping/domain/PortfolioProductType.java @@ -44,4 +44,12 @@ public enum PortfolioProductType { return type; } + public boolean isSavingProduct() { + return this.value.equals(PortfolioProductType.SAVING.getValue()); + } + + public boolean isLoanProduct() { + return this.value.equals(PortfolioProductType.LOAN.getValue()); + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java new file mode 100755 index 000000000..039768319 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleApiResource.java @@ -0,0 +1,148 @@ +/** + * 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.rule.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.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService; +import org.mifosplatform.accounting.rule.data.AccountingRuleData; +import org.mifosplatform.accounting.rule.service.AccountingRuleReadPlatformService; +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.OfficeData; +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("/accountingrules") +@Component +@Scope("singleton") +public class AccountingRuleApiResource { + + private static final Set RESPONSE_DATA_PARAMETERS = new HashSet(Arrays.asList("id", "officeId", "officeName", + "accountToDebitId", "accountToCreditId", "name", "description", "systemDefined")); + + private final String resourceNameForPermission = "ACCOUNTINGRULE"; + + private final AccountingRuleReadPlatformService accountingRuleReadPlatformService; + private final GLAccountReadPlatformService accountReadPlatformService; + private final OfficeReadPlatformService officeReadPlatformService; + private final DefaultToApiJsonSerializer apiJsonSerializerService; + private final ApiRequestParameterHelper apiRequestParameterHelper; + private final PlatformSecurityContext context; + private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService; + + @Autowired + public AccountingRuleApiResource(final PlatformSecurityContext context, + final AccountingRuleReadPlatformService accountingRuleReadPlatformService, + final DefaultToApiJsonSerializer toApiJsonSerializer, + final ApiRequestParameterHelper apiRequestParameterHelper, final GLAccountReadPlatformService accountReadPlatformService, + final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, + final OfficeReadPlatformService officeReadPlatformService) { + this.context = context; + this.apiRequestParameterHelper = apiRequestParameterHelper; + this.commandsSourceWritePlatformService = commandsSourceWritePlatformService; + this.apiJsonSerializerService = toApiJsonSerializer; + this.accountingRuleReadPlatformService = accountingRuleReadPlatformService; + this.officeReadPlatformService = officeReadPlatformService; + this.accountReadPlatformService = accountReadPlatformService; + } + + @GET + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retrieveAllAccountingRules(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); + final List accountingRuleDatas = this.accountingRuleReadPlatformService.retrieveAllAccountingRules(officeId); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + return this.apiJsonSerializerService.serialize(settings, accountingRuleDatas, RESPONSE_DATA_PARAMETERS); + } + + @GET + @Path("{accountingRuleId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String retreiveAccountingRule(@PathParam("accountingRuleId") final Long accountingRuleId, @Context final UriInfo uriInfo) { + + this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); + + final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); + + final AccountingRuleData accountingRuleData = this.accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId); + if (settings.isTemplate()) { + accountingRuleData.setAllowedOffices(new ArrayList(this.officeReadPlatformService.retrieveAllOfficesForDropdown())); + accountingRuleData.setAllowedAccounts(new ArrayList(this.accountReadPlatformService + .retrieveAllEnabledDetailGLAccounts())); + } + + return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS); + } + + @POST + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String createAccountingRule(final String jsonRequestBody) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountingRule().withJson(jsonRequestBody).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.apiJsonSerializerService.serialize(result); + } + + @PUT + @Path("{accountingRuleId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String updateAccountingRule(@PathParam("accountingRuleId") final Long accountingRuleId, final String jsonRequestBody) { + final CommandWrapper commandRequest = new CommandWrapperBuilder().updateAccountingRule(accountingRuleId).withJson(jsonRequestBody) + .build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.apiJsonSerializerService.serialize(result); + } + + @DELETE + @Path("{accountingRuleId}") + @Consumes({ MediaType.APPLICATION_JSON }) + @Produces({ MediaType.APPLICATION_JSON }) + public String deleteAccountingRule(@PathParam("accountingRuleId") final Long accountingRuleId) { + + final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteAccountingRule(accountingRuleId).build(); + + final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest); + + return this.apiJsonSerializerService.serialize(result); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleJsonInputParams.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleJsonInputParams.java new file mode 100755 index 000000000..ce8be9640 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/api/AccountingRuleJsonInputParams.java @@ -0,0 +1,38 @@ +package org.mifosplatform.accounting.rule.api; + +import java.util.HashSet; +import java.util.Set; + +/*** + * Enum of all parameters passed in while creating/updating a loan product + ***/ +public enum AccountingRuleJsonInputParams { + ID("id"), OFFICE_ID("officeId"), ACCOUNT_TO_DEBIT("accountToDebitId"), ACCOUNT_TO_CREDIT("accountToCreditId"), NAME("name"), DESCRIPTION( + "description"), SYSTEM_DEFINED("systemDefined"); + + private final String value; + + private AccountingRuleJsonInputParams(final String value) { + this.value = value; + } + + private static final Set values = new HashSet(); + static { + for (final AccountingRuleJsonInputParams type : AccountingRuleJsonInputParams.values()) { + values.add(type.value); + } + } + + public static Set getAllValues() { + return values; + } + + @Override + public String toString() { + return name().toString().replaceAll("_", " "); + } + + public String getValue() { + return this.value; + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/command/AccountingRuleCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/command/AccountingRuleCommand.java new file mode 100755 index 000000000..205839977 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/command/AccountingRuleCommand.java @@ -0,0 +1,79 @@ +/** + * 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.rule.command; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; + +/** + * Immutable command for adding an accounting closure + */ +public class AccountingRuleCommand { + + @SuppressWarnings("unused") + private final Long id; + private final Long officeId; + private final Long accountToDebitId; + private final Long accountToCreditId; + private final String name; + private final String description; + + public AccountingRuleCommand(Long id, Long officeId, Long accountToDebitId, Long accountToCreditId, String name, String description) { + this.id = id; + this.officeId = officeId; + this.accountToDebitId = accountToDebitId; + this.accountToCreditId = accountToCreditId; + this.name = name; + this.description = description; + } + + public void validateForCreate() { + + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule"); + + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(this.accountToDebitId) + .notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(this.accountToCreditId) + .notNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).ignoreIfNull() + .integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(this.name).notBlank() + .notExceedingLengthOf(100); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull() + .notExceedingLengthOf(500); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + + public void validateForUpdate() { + final List dataValidationErrors = new ArrayList(); + + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule"); + + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(this.accountToDebitId) + .ignoreIfNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(this.accountToCreditId) + .ignoreIfNull().integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).ignoreIfNull() + .integerGreaterThanZero(); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(this.name).ignoreIfNull().notBlank() + .notExceedingLengthOf(100); + baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(this.description).ignoreIfNull() + .notExceedingLengthOf(500); + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", + "Validation errors exist.", dataValidationErrors); } + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/data/AccountingRuleData.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/data/AccountingRuleData.java new file mode 100755 index 000000000..68e6b22bb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/data/AccountingRuleData.java @@ -0,0 +1,76 @@ +/** + * 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.rule.data; + +import java.util.ArrayList; +import java.util.List; + +import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.organisation.office.data.OfficeData; + +/** + * Immutable object representing a General Ledger Account + * + * Note: no getter/setters required as google-gson will produce json from fields + * of object. + */ +public class AccountingRuleData { + + @SuppressWarnings("unused") + private final Long id; + @SuppressWarnings("unused") + private final Long officeId; + @SuppressWarnings("unused") + private final String officeName; + @SuppressWarnings("unused") + private final Long accountToDebitId; + @SuppressWarnings("unused") + private final Long accountToCreditId; + @SuppressWarnings("unused") + private final String name; + @SuppressWarnings("unused") + private final String description; + @SuppressWarnings("unused") + private final boolean systemDefined; + + // template + @SuppressWarnings("unused") + private List allowedOffices = new ArrayList(); + @SuppressWarnings("unused") + private List allowedAccounts = new ArrayList(); + + public AccountingRuleData(Long id, Long accountToDebitId, Long accountToCreditId) { + this(id, null, null, accountToDebitId, accountToCreditId, null, null, true, null, null); + } + + public AccountingRuleData(Long id, Long officeId, String officeName, Long accountToDebitId, Long accountToCreditId, String name, + String description, boolean systemDefined) { + this(id, officeId, officeName, accountToDebitId, accountToCreditId, name, description, systemDefined, null, null); + } + + public AccountingRuleData(Long id, Long officeId, String officeName, Long accountToDebitId, Long accountToCreditId, String name, + String description, boolean systemDefined, List allowedOffices, List allowedAccounts) { + this.id = id; + this.officeId = officeId; + this.officeName = officeName; + this.accountToDebitId = accountToDebitId; + this.accountToCreditId = accountToCreditId; + this.name = name; + this.description = description; + this.systemDefined = systemDefined; + this.allowedOffices = allowedOffices; + this.allowedAccounts = allowedAccounts; + } + + public void setAllowedOffices(List allowedOffices) { + this.allowedOffices = allowedOffices; + } + + public void setAllowedAccounts(List allowedAccounts) { + this.allowedAccounts = allowedAccounts; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRule.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRule.java new file mode 100755 index 000000000..e93e62fa5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRule.java @@ -0,0 +1,150 @@ +/** + * 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.rule.domain; + +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.UniqueConstraint; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.accounting.glaccount.domain.GLAccount; +import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.organisation.office.domain.Office; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "acc_accounting_rule", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "accounting_rule_name_unique") }) +public class AccountingRule extends AbstractPersistable { + + @Column(name = "name", nullable = false, length = 500) + private String name; + + @ManyToOne + @JoinColumn(name = "office_id", nullable = true) + private Office office; + + @ManyToOne + @JoinColumn(name = "debit_account_id", nullable = false) + private GLAccount accountToDebit; + + @ManyToOne + @JoinColumn(name = "credit_account_id", nullable = false) + private GLAccount accountToCredit; + + @Column(name = "description", nullable = true, length = 500) + private String description; + + @Column(name = "system_defined", nullable = false) + private Boolean systemDefined; + + protected AccountingRule() {} + + private AccountingRule(Office office, GLAccount accountToDebit, GLAccount accountToCredit, String name, String description, + boolean systemDefined) { + this.accountToDebit = accountToDebit; + this.accountToCredit = accountToCredit; + this.name = name; + this.office = office; + this.description = StringUtils.defaultIfEmpty(description, null); + if (this.description != null) { + this.description = this.description.trim(); + } + this.systemDefined = systemDefined; + } + + public static AccountingRule fromJson(final Office office, final GLAccount accountToDebit, final GLAccount accountToCredit, + JsonCommand command) { + final String name = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue()); + final String description = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue()); + final boolean systemDefined = false; + return new AccountingRule(office, accountToDebit, accountToCredit, name, description, systemDefined); + } + + public Map update(final JsonCommand command) { + final Map actualChanges = new LinkedHashMap(5); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.OFFICE_ID.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(), 0L); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.NAME.getValue(), this.name); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.DESCRIPTION.getValue(), this.description); + handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue(), this.systemDefined); + return actualChanges; + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + final String propertyToBeUpdated) { + if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) { + final String newValue = command.stringValueOfParameterNamed(paramName); + actualChanges.put(paramName, newValue); + // now update actual property + if (paramName.equals(AccountingRuleJsonInputParams.DESCRIPTION.getValue())) { + this.description = newValue; + } else if (paramName.equals(AccountingRuleJsonInputParams.NAME.getValue())) { + this.name = newValue; + } + } + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + final boolean propertyToBeUpdated) { + if (command.isChangeInBooleanParameterNamed(paramName, propertyToBeUpdated)) { + final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramName); + actualChanges.put(paramName, newValue); + // now update actual property + if (paramName.equals(AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue())) { + this.systemDefined = newValue; + } + } + } + + private void handlePropertyUpdate(final JsonCommand command, final Map actualChanges, final String paramName, + final Long propertyToBeUpdated) { + if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) { + final Long newValue = command.longValueOfParameterNamed(paramName); + actualChanges.put(paramName, newValue); + // now update actual property + if (paramName.equals(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) { + // do nothing as this is a nested property + } else if (paramName.equals(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) { + // do nothing as this is a nested property + } + } + } + + public void setOffice(Office office) { + this.office = office; + } + + public Office getOffice() { + return this.office; + } + + public GLAccount getAccountToDebit() { + return this.accountToDebit; + } + + public GLAccount getAccountToCredit() { + return this.accountToCredit; + } + + public void setAccountToDebit(GLAccount accountToDebit) { + this.accountToDebit = accountToDebit; + } + + public void setAccountToCredit(GLAccount accountToCredit) { + this.accountToCredit = accountToCredit; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepository.java new file mode 100755 index 000000000..67200f3da --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepository.java @@ -0,0 +1,17 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.rule.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface AccountingRuleRepository extends JpaRepository, JpaSpecificationExecutor { + + @Query("from AccountingRule accountingRule where accountingRule.office is null or accountingRule.office.id =:officeId") + AccountingRule getAccountingRuleByOfficeId(@Param("officeId") Long officeId); +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepositoryWrapper.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepositoryWrapper.java new file mode 100755 index 000000000..d597512c5 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingRuleRepositoryWrapper.java @@ -0,0 +1,33 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.rule.domain; + +import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *

+ * Wrapper for {@link AccountingRuleRepository} . + *

+ */ +@Service +public class AccountingRuleRepositoryWrapper { + + private final AccountingRuleRepository repository; + + @Autowired + public AccountingRuleRepositoryWrapper(final AccountingRuleRepository repository) { + this.repository = repository; + } + + public AccountingRule findOneWithNotFoundDetection(final Long id) { + final AccountingRule accountingRule = this.repository.findOne(id); + if (accountingRule == null) { throw new AccountingRuleNotFoundException(id); } + return accountingRule; + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleDuplicateException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleDuplicateException.java new file mode 100755 index 000000000..a5023731f --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleDuplicateException.java @@ -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.rule.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when an Accounting rule with a given name + * already exists + */ +public class AccountingRuleDuplicateException extends AbstractPlatformDomainRuleException { + + public AccountingRuleDuplicateException(String name) { + super("error.msg.accounting.rule.duplicate", "An accounting rule with the name " + name + " already exists" + name); + } + +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidDeleteException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidDeleteException.java new file mode 100755 index 000000000..fc1dbf2fb --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidDeleteException.java @@ -0,0 +1,21 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.rule.exception; + +import java.util.Date; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when a GL Closure Delte command is invalid + */ +public class AccountingRuleInvalidDeleteException extends AbstractPlatformDomainRuleException { + + public AccountingRuleInvalidDeleteException(final Long officeId, final String officeName, final Date latestclosureDate) { + super("error.msg.glclosure.invalid.delete", "The latest closure for office with Id " + officeId + " and name " + officeName + + " is on " + latestclosureDate.toString() + ", please delete this closure first", latestclosureDate); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidException.java new file mode 100755 index 000000000..cc2b61b69 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleInvalidException.java @@ -0,0 +1,39 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.rule.exception; + +import java.util.Date; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; + +/** + * A {@link RuntimeException} thrown when a GL Closure is Invalid + */ +public class AccountingRuleInvalidException extends AbstractPlatformDomainRuleException { + + /*** enum of reasons for invalid Accounting Closure **/ + public static enum GL_CLOSURE_INVALID_REASON { + FUTURE_DATE, ACCOUNTING_CLOSED; + + public String errorMessage() { + if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { + return "Accounting closures cannot be made for a future date"; + } else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "Accounting Closure for this branch has already been defined for a greater date"; } + return name().toString(); + } + + public String errorCode() { + if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { + return "error.msg.glclosure.invalid.future.date"; + } else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) { return "error.msg.glclosure.invalid.accounting.closed"; } + return name().toString(); + } + } + + public AccountingRuleInvalidException(final GL_CLOSURE_INVALID_REASON reason, final Date date) { + super(reason.errorCode(), reason.errorMessage(), date); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleNotFoundException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleNotFoundException.java new file mode 100755 index 000000000..f5d3c0442 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/exception/AccountingRuleNotFoundException.java @@ -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.rule.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * A {@link RuntimeException} thrown when Accounting rule resources are not + * found. + */ +public class AccountingRuleNotFoundException extends AbstractPlatformResourceNotFoundException { + + public AccountingRuleNotFoundException(final Long id) { + super("error.msg.accounting.rule.id.invalid", "Accounting Rule with identifier " + id + " does not exist", id); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/CreateAccountingRuleCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/CreateAccountingRuleCommandHandler.java new file mode 100755 index 000000000..bb5fe8966 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/CreateAccountingRuleCommandHandler.java @@ -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.rule.handler; + +import org.mifosplatform.accounting.rule.service.AccountingRuleWritePlatformService; +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 CreateAccountingRuleCommandHandler implements NewCommandSourceHandler { + + private final AccountingRuleWritePlatformService writePlatformService; + + @Autowired + public CreateAccountingRuleCommandHandler(final AccountingRuleWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.createAccountingRule(command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/DeleteAccountingRuleCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/DeleteAccountingRuleCommandHandler.java new file mode 100755 index 000000000..dd6d9c996 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/DeleteAccountingRuleCommandHandler.java @@ -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.rule.handler; + +import org.mifosplatform.accounting.rule.service.AccountingRuleWritePlatformService; +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 DeleteAccountingRuleCommandHandler implements NewCommandSourceHandler { + + private final AccountingRuleWritePlatformService writePlatformService; + + @Autowired + public DeleteAccountingRuleCommandHandler(final AccountingRuleWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.deleteAccountingRule(command.entityId()); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/UpdateAccountingRuleCommandHandler.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/UpdateAccountingRuleCommandHandler.java new file mode 100755 index 000000000..f0da2a337 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/handler/UpdateAccountingRuleCommandHandler.java @@ -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.rule.handler; + +import org.mifosplatform.accounting.rule.service.AccountingRuleWritePlatformService; +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 UpdateAccountingRuleCommandHandler implements NewCommandSourceHandler { + + private final AccountingRuleWritePlatformService writePlatformService; + + @Autowired + public UpdateAccountingRuleCommandHandler(final AccountingRuleWritePlatformService writePlatformService) { + this.writePlatformService = writePlatformService; + } + + @Transactional + @Override + public CommandProcessingResult processCommand(final JsonCommand command) { + return this.writePlatformService.updateAccountingRule(command.entityId(), command); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/serialization/AccountingRuleCommandFromApiJsonDeserializer.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/serialization/AccountingRuleCommandFromApiJsonDeserializer.java new file mode 100755 index 000000000..56192ff22 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/serialization/AccountingRuleCommandFromApiJsonDeserializer.java @@ -0,0 +1,62 @@ +/** + * 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.rule.serialization; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams; +import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams; +import org.mifosplatform.accounting.rule.command.AccountingRuleCommand; +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 AccountingRuleCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer { + + private final FromJsonHelper fromApiJsonHelper; + + @Autowired + public AccountingRuleCommandFromApiJsonDeserializer(final FromJsonHelper fromApiJsonfromApiJsonHelper) { + this.fromApiJsonHelper = fromApiJsonfromApiJsonHelper; + } + + @Override + public AccountingRuleCommand commandFromApiJson(final String json) { + if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } + + final Type typeOfMap = new TypeToken>() {}.getType(); + final Set supportedParameters = GLClosureJsonInputParams.getAllValues(); + this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters); + + final JsonElement element = this.fromApiJsonHelper.parse(json); + + final Long id = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ID.getValue(), element); + final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element); + final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(), + element); + final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(), + element); + final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element); + final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element); + + return new AccountingRuleCommand(id, officeId, accountToDebitId, accountToCreditId, name, description); + } +} \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java new file mode 100755 index 000000000..d33b7fef7 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformService.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.accounting.rule.service; + +import java.util.List; + +import org.mifosplatform.accounting.rule.data.AccountingRuleData; + +public interface AccountingRuleReadPlatformService { + + List retrieveAllAccountingRules(Long OfficeId); + + AccountingRuleData retrieveAccountingRuleById(long accountingRuleId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java new file mode 100755 index 000000000..7324bf630 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleReadPlatformServiceImpl.java @@ -0,0 +1,87 @@ +/** + * 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.rule.service; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import org.mifosplatform.accounting.rule.data.AccountingRuleData; +import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException; +import org.mifosplatform.infrastructure.core.domain.JdbcSupport; +import org.mifosplatform.infrastructure.core.service.TenantAwareRoutingDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.stereotype.Service; + +@Service +public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleReadPlatformService { + + private final JdbcTemplate jdbcTemplate; + + @Autowired + public AccountingRuleReadPlatformServiceImpl(final TenantAwareRoutingDataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + private static final class AccountingRuleMapper implements RowMapper { + + private final String schemaSql; + + public AccountingRuleMapper() { + final StringBuilder sqlBuilder = new StringBuilder(400); + sqlBuilder.append("select rule.id as id,rule.name as name, rule.office_id as officeId,office.name as officeName,") + .append(" rule.debit_account_id as debitAccountId,rule.credit_account_id as creditAccountId,") + .append(" rule.description as description, rule.system_defined as systemDefined ").append("from ") + .append(" acc_accounting_rule rule left join m_office office on rule.office_id=office.id"); + this.schemaSql = sqlBuilder.toString(); + } + + public String schema() { + return this.schemaSql; + } + + @Override + public AccountingRuleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long officeId = JdbcSupport.getLong(rs, "officeId"); + final String officeName = rs.getString("officeName"); + final String name = rs.getString("name"); + final String description = rs.getString("description"); + final Long accountToDebitId = rs.getLong("debitAccountId"); + final Long accountToCreditId = rs.getLong("creditAccountId"); + final boolean systemDefined = rs.getBoolean("systemDefined"); + + return new AccountingRuleData(id, officeId, officeName, accountToDebitId, accountToCreditId, name, description, systemDefined); + } + } + + @Override + public List retrieveAllAccountingRules(Long officeId) { + final AccountingRuleMapper rm = new AccountingRuleMapper(); + String sql = "select " + rm.schema() + " where system_defined=0 and office.id = ?"; + sql = sql + " order by rule.id asc"; + return this.jdbcTemplate.query(sql, rm, new Object[] { officeId }); + } + + @Override + public AccountingRuleData retrieveAccountingRuleById(long accountingRuleId) { + try { + + final AccountingRuleMapper rm = new AccountingRuleMapper(); + final String sql = "select " + rm.schema() + " and glClosure.id = ?"; + + final AccountingRuleData accountingRuleData = this.jdbcTemplate.queryForObject(sql, rm, new Object[] { accountingRuleId }); + return accountingRuleData; + } catch (final EmptyResultDataAccessException e) { + throw new AccountingRuleNotFoundException(accountingRuleId); + } + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformService.java new file mode 100755 index 000000000..7b4b833ac --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformService.java @@ -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.rule.service; + +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; + +public interface AccountingRuleWritePlatformService { + + CommandProcessingResult createAccountingRule(JsonCommand command); + + CommandProcessingResult updateAccountingRule(Long accountingRuleId, JsonCommand command); + + CommandProcessingResult deleteAccountingRule(Long accountingRuleId); + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java new file mode 100755 index 000000000..608c07f6c --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java @@ -0,0 +1,157 @@ +/** + * 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.rule.service; + +import java.util.Map; + +import org.mifosplatform.accounting.closure.api.GLClosureJsonInputParams; +import org.mifosplatform.accounting.glaccount.domain.GLAccount; +import org.mifosplatform.accounting.glaccount.domain.GLAccountRepositoryWrapper; +import org.mifosplatform.accounting.rule.api.AccountingRuleJsonInputParams; +import org.mifosplatform.accounting.rule.command.AccountingRuleCommand; +import org.mifosplatform.accounting.rule.domain.AccountingRule; +import org.mifosplatform.accounting.rule.domain.AccountingRuleRepository; +import org.mifosplatform.accounting.rule.domain.AccountingRuleRepositoryWrapper; +import org.mifosplatform.accounting.rule.exception.AccountingRuleDuplicateException; +import org.mifosplatform.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer; +import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; +import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; +import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException; +import org.mifosplatform.organisation.office.domain.Office; +import org.mifosplatform.organisation.office.domain.OfficeRepository; +import org.mifosplatform.organisation.office.exception.OfficeNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements AccountingRuleWritePlatformService { + + private final static Logger logger = LoggerFactory.getLogger(AccountingRuleWritePlatformServiceJpaRepositoryImpl.class); + + private final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper; + private final AccountingRuleRepository accountingRuleRepository; + private final GLAccountRepositoryWrapper accountRepositoryWrapper; + private final OfficeRepository officeRepository; + private final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer; + + @Autowired + public AccountingRuleWritePlatformServiceJpaRepositoryImpl(final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper, + final GLAccountRepositoryWrapper accountRepositoryWrapper, final OfficeRepository officeRepository, + final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer, final AccountingRuleRepository ruleRepository) { + this.accountRepositoryWrapper = accountRepositoryWrapper; + this.officeRepository = officeRepository; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; + this.accountingRuleRepository = ruleRepository; + this.accountingRuleRepositoryWrapper = accountingRuleRepositoryWrapper; + } + + /** + * @param command + * @param dve + */ + private void handleAccountingRuleIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { + final Throwable realCause = dve.getMostSpecificCause(); + if (realCause.getMessage().contains("accounting_rule_name_unique")) { throw new AccountingRuleDuplicateException( + command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue())); } + + logger.error(dve.getMessage(), dve); + throw new PlatformDataIntegrityException("error.msg.accounting.rule.unknown.data.integrity.issue", + "Unknown data integrity issue with resource Accounting Rule: " + realCause.getMessage()); + } + + @Transactional + @Override + public CommandProcessingResult createAccountingRule(JsonCommand command) { + try { + final AccountingRuleCommand accountingRuleCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); + accountingRuleCommand.validateForCreate(); + + // check office is valid + final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue()); + Office office = null; + if (officeId != null) { + office = this.officeRepository.findOne(officeId); + if (office == null) { throw new OfficeNotFoundException(officeId); } + } + + // get the GL Accounts to Debit and Credit + final Long accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()); + final Long accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()); + final GLAccount accountToDebit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId); + final GLAccount accountToCredit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId); + + final AccountingRule accountingRule = AccountingRule.fromJson(office, accountToDebit, accountToCredit, command); + this.accountingRuleRepository.saveAndFlush(accountingRule); + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId) + .withEntityId(accountingRule.getId()).build(); + } catch (final DataIntegrityViolationException dve) { + handleAccountingRuleIntegrityIssues(command, dve); + return CommandProcessingResult.empty(); + } + } + + @Transactional + @Override + public CommandProcessingResult updateAccountingRule(Long accountingRuleId, JsonCommand command) { + final AccountingRuleCommand accountingRuleCommand = this.fromApiJsonDeserializer.commandFromApiJson(command.json()); + accountingRuleCommand.validateForUpdate(); + + Long officeId = null; + if (command.parameterExists(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) { + officeId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue()); + } + + Long accountToDebitId = null; + if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) { + accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()); + } + + Long accountToCreditId = null; + if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) { + accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()); + } + + final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId); + final Map changesOnly = accountingRule.update(command); + + if (changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) { + final GLAccount accountToDebit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId); + accountingRule.setAccountToDebit(accountToDebit); + } + + if (changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) { + final GLAccount accountToCredit = accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId); + accountingRule.setAccountToCredit(accountToCredit); + } + + if (changesOnly.containsKey(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) { + final Office userOffice = this.officeRepository.findOne(officeId); + if (userOffice == null) { throw new OfficeNotFoundException(officeId); } + accountingRule.setOffice(userOffice); + } + + if (!changesOnly.isEmpty()) { + this.accountingRuleRepository.saveAndFlush(accountingRule); + } + + return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(accountingRule.getId()) + .with(changesOnly).build(); + } + + @Transactional + @Override + public CommandProcessingResult deleteAccountingRule(Long accountingRuleId) { + final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId); + this.accountingRuleRepository.delete(accountingRule); + return new CommandProcessingResultBuilder().withEntityId(accountingRule.getId()).build(); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java index f1437c0ab..b1abe2ab4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/commands/service/CommandWrapperBuilder.java @@ -932,4 +932,28 @@ public class CommandWrapperBuilder { this.href = "/centers/" + centerId + "?command=activate"; return this; } + + public CommandWrapperBuilder createAccountingRule() { + this.actionName = "CREATE"; + this.entityName = "ACCOUNTINGRULE"; + this.entityId = null; + this.href = "/accountingrules/template"; + return this; + } + + public CommandWrapperBuilder updateAccountingRule(final Long accountingRuleId) { + this.actionName = "UPDATE"; + this.entityName = "ACCOUNTINGRULE"; + this.entityId = accountingRuleId; + this.href = "/accountingrules/" + accountingRuleId; + return this; + } + + public CommandWrapperBuilder deleteAccountingRule(final Long accountingRuleId) { + this.actionName = "DELETE"; + this.entityName = "ACCOUNTINGRULE"; + this.entityId = accountingRuleId; + this.href = "/accountingrules/" + accountingRuleId; + return this; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java index bec4a0be0..91da78ebc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/charge/data/ChargeData.java @@ -53,6 +53,22 @@ public class ChargeData implements Comparable { null, null, null); } + public static ChargeData lookup(final Long id, final String name, final boolean isPenalty) { + BigDecimal amount = null; + CurrencyData currency = null; + EnumOptionData chargeTimeType = null; + EnumOptionData chargeAppliesTo = null; + EnumOptionData chargeCalculationType = null; + Boolean penalty = isPenalty; + Boolean active = false; + Collection currencyOptions = null; + List chargeCalculationTypeOptions = null; + List chargeAppliesToOptions = null; + List chargeTimeTypeOptions = null; + return new ChargeData(id, name, amount, currency, chargeTimeType, chargeAppliesTo, chargeCalculationType, penalty, active, + currencyOptions, chargeCalculationTypeOptions, chargeAppliesToOptions, chargeTimeTypeOptions); + } + private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency, final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType, final boolean penalty, final boolean active, final Collection currencyOptions,