wip:accounting stubs for autoposting and abstractions

This commit is contained in:
Vishwas Babu AJ
2013-04-30 20:58:25 +05:30
parent b27b574d76
commit b35d7a6249
47 changed files with 2361 additions and 11 deletions
@@ -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;
@@ -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<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(Arrays.asList("id", "officeId", "officeName",
"closingDate", "deleted", "createdDate", "lastUpdatedDate", "createdByUserId", "createdByUsername", "lastUpdatedByUserId",
"lastUpdatedByUsername"));
private final String resourceNameForPermission = "GLCLOSURE";
private final GLClosureReadPlatformService glClosureReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService;
private final DefaultToApiJsonSerializer<GLClosureData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public AutoPostingApiResource(final PlatformSecurityContext context, final GLClosureReadPlatformService glClosureReadPlatformService,
final DefaultToApiJsonSerializer<GLClosureData> toApiJsonSerializer, final ApiRequestParameterHelper apiRequestParameterHelper,
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
final OfficeReadPlatformService officeReadPlatformService) {
this.context = context;
this.apiRequestParameterHelper = apiRequestParameterHelper;
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
this.apiJsonSerializerService = toApiJsonSerializer;
this.glClosureReadPlatformService = glClosureReadPlatformService;
this.officeReadPlatformService = officeReadPlatformService;
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retrieveAllClosures(@Context final UriInfo uriInfo, @QueryParam("officeId") final Long officeId) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final List<GLClosureData> glClosureDatas = this.glClosureReadPlatformService.retrieveAllGLClosures(officeId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, glClosureDatas, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{glClosureId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public String retreiveClosure(@PathParam("glClosureId") final Long glClosureId, @Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
final GLClosureData glClosureData = this.glClosureReadPlatformService.retrieveGLClosureById(glClosureId);
if (settings.isTemplate()) {
// glClosureData.setAllowedOffices(new
// ArrayList<OfficeLookup>(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);
}
}
@@ -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<String> values = new HashSet<String>();
static {
for (final AutoPostingJsonInputParams type : AutoPostingJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter(GLClosureJsonInputParams.CLOSING_DATE.getValue()).value(this.closingDate).notBlank();
baseDataValidator.reset().parameter(GLClosureJsonInputParams.OFFICE_ID.getValue()).value(this.officeId).notNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull()
.notExceedingLengthOf(500);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
public void validateForUpdate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLClosure");
baseDataValidator.reset().parameter(GLClosureJsonInputParams.COMMENTS.getValue()).value(this.comments).ignoreIfNull()
.notExceedingLengthOf(500);
baseDataValidator.reset().anyOfNotNull(this.comments);
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
"Validation errors exist.", dataValidationErrors); }
}
}
@@ -0,0 +1,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<EnumOptionData> allowedProductTypes = new ArrayList<EnumOptionData>();
@SuppressWarnings("unused")
private List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
@SuppressWarnings("unused")
private List<SavingsProductData> allowedSavingProducts = new ArrayList<SavingsProductData>();
@SuppressWarnings("unused")
private List<LoanProductData> allowedLoanProducts = new ArrayList<LoanProductData>();
@SuppressWarnings("unused")
private List<CodeValue> allowedEventAttributes = new ArrayList<CodeValue>();
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<EnumOptionData> allowedProductTypes,
List<OfficeData> allowedOffices, List<SavingsProductData> allowedSavingProducts, List<LoanProductData> allowedLoanProducts,
List<CodeValue> 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<EnumOptionData> allowedProductTypes) {
this.allowedProductTypes = allowedProductTypes;
}
public void setAllowedOffices(List<OfficeData> allowedOffices) {
this.allowedOffices = allowedOffices;
}
public void setAllowedSavingProducts(List<SavingsProductData> allowedSavingProducts) {
this.allowedSavingProducts = allowedSavingProducts;
}
public void setAllowedLoanProducts(List<LoanProductData> allowedLoanProducts) {
this.allowedLoanProducts = allowedLoanProducts;
}
public void setAllowedEventAttributes(List<CodeValue> allowedEventAttributes) {
this.allowedEventAttributes = allowedEventAttributes;
}
}
@@ -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<Long> {
@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<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(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<String, Object> actualChanges, final String paramName,
final String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AutoPostingJsonInputParams.DESCRIPTION.getValue())) {
this.description = newValue;
} else if (paramName.equals(AutoPostingJsonInputParams.NAME.getValue())) {
this.name = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> 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<String, Object> actualChanges, final String paramName,
final Long propertyToBeUpdated) {
if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) {
final Long newValue = command.longValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(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;
}
}
@@ -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<AutoPosting, Long>, JpaSpecificationExecutor<AutoPosting> {
@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);
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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<GLClosureCommand> {
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<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = GLClosureJsonInputParams.getAllValues();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long id = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.ID.getValue(), element);
final Long officeId = this.fromApiJsonHelper.extractLongNamed(GLClosureJsonInputParams.OFFICE_ID.getValue(), element);
final String comments = this.fromApiJsonHelper.extractStringNamed(GLClosureJsonInputParams.COMMENTS.getValue(), element);
final LocalDate closingDate = this.fromApiJsonHelper.extractLocalDateNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue(),
element);
return new GLClosureCommand(id, officeId, closingDate, comments);
}
}
@@ -0,0 +1,18 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.autoposting.service;
import java.util.List;
import org.mifosplatform.accounting.autoposting.data.AutoPostingData;
public interface AutoPostingReadPlatformService {
List<AutoPostingData> retrieveAllAutoPostingRules();
AutoPostingData retrieveAutoPostingRuleById(long autopostingId);
}
@@ -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<AutoPostingData> {
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<AutoPostingData> 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);
}
}
}
@@ -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);
}
@@ -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<String, Object> changesOnly = glClosure.update(command);
if (!changesOnly.isEmpty()) {
this.glClosureRepository.saveAndFlush(glClosure);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(glClosure.getOffice().getId())
.withEntityId(glClosure.getId()).with(changesOnly).build();
}
@Transactional
@Override
public CommandProcessingResult 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());
}
}
@@ -44,10 +44,6 @@ public class GLClosure extends AbstractAuditableCustom<AppUser, Long> {
@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() {
//
}
@@ -59,11 +59,6 @@ public class GLAccount extends AbstractPersistable<Long> {
@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() {
//
}
@@ -0,0 +1,33 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.mifosplatform.accounting.glaccount.domain;
import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link GLAccountRepository} .
* </p>
*/
@Service
public class GLAccountRepositoryWrapper {
private final GLAccountRepository repository;
@Autowired
public GLAccountRepositoryWrapper(final GLAccountRepository repository) {
this.repository = repository;
}
public GLAccount findOneWithNotFoundDetection(final Long id) {
final GLAccount account = this.repository.findOne(id);
if (account == null) { throw new GLAccountNotFoundException(id); }
return account;
}
}
@@ -17,8 +17,9 @@ public interface GLAccountReadPlatformService {
GLAccountData retrieveGLAccountById(long glAccountId);
List<GLAccountData> retrieveAllEnabledDetailGLAccounts();
List<GLAccountData> retrieveAllEnabledDetailGLAccounts(GLAccountType accountType);
GLAccountData retrieveNewGLAccountDetails();
}
@@ -166,6 +166,11 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false);
}
@Override
public List<GLAccountData> 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; }
@@ -56,7 +56,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@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)
@@ -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());
}
}
@@ -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<String> RESPONSE_DATA_PARAMETERS = new HashSet<String>(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<AccountingRuleData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
@Autowired
public AccountingRuleApiResource(final PlatformSecurityContext context,
final AccountingRuleReadPlatformService accountingRuleReadPlatformService,
final DefaultToApiJsonSerializer<AccountingRuleData> 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<AccountingRuleData> 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<OfficeData>(this.officeReadPlatformService.retrieveAllOfficesForDropdown()));
accountingRuleData.setAllowedAccounts(new ArrayList<GLAccountData>(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);
}
}
@@ -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<String> values = new HashSet<String>();
static {
for (final AccountingRuleJsonInputParams type : AccountingRuleJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -0,0 +1,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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>();
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); }
}
}
@@ -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<OfficeData> allowedOffices = new ArrayList<OfficeData>();
@SuppressWarnings("unused")
private List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
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<OfficeData> allowedOffices, List<GLAccountData> 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<OfficeData> allowedOffices) {
this.allowedOffices = allowedOffices;
}
public void setAllowedAccounts(List<GLAccountData> allowedAccounts) {
this.allowedAccounts = allowedAccounts;
}
}
@@ -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<Long> {
@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<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<String, Object>(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<String, Object> actualChanges, final String paramName,
final String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AccountingRuleJsonInputParams.DESCRIPTION.getValue())) {
this.description = newValue;
} else if (paramName.equals(AccountingRuleJsonInputParams.NAME.getValue())) {
this.name = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final boolean propertyToBeUpdated) {
if (command.isChangeInBooleanParameterNamed(paramName, propertyToBeUpdated)) {
final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue())) {
this.systemDefined = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final Long propertyToBeUpdated) {
if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) {
final Long newValue = command.longValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(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;
}
}
@@ -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<AccountingRule, Long>, JpaSpecificationExecutor<AccountingRule> {
@Query("from AccountingRule accountingRule where accountingRule.office is null or accountingRule.office.id =:officeId")
AccountingRule getAccountingRuleByOfficeId(@Param("officeId") Long officeId);
}
@@ -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;
/**
* <p>
* Wrapper for {@link AccountingRuleRepository} .
* </p>
*/
@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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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<AccountingRuleCommand> {
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<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = GLClosureJsonInputParams.getAllValues();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long id = this.fromApiJsonHelper.extractLongNamed(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);
}
}
@@ -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<AccountingRuleData> retrieveAllAccountingRules(Long OfficeId);
AccountingRuleData retrieveAccountingRuleById(long accountingRuleId);
}
@@ -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<AccountingRuleData> {
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<AccountingRuleData> 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);
}
}
}
@@ -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);
}
@@ -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<String, Object> 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();
}
}
@@ -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;
}
}
@@ -53,6 +53,22 @@ public class ChargeData implements Comparable<ChargeData> {
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<CurrencyData> currencyOptions = null;
List<EnumOptionData> chargeCalculationTypeOptions = null;
List<EnumOptionData> chargeAppliesToOptions = null;
List<EnumOptionData> 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<CurrencyData> currencyOptions,