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) @Column(name = "comments", nullable = true, length = 500)
private String comments; private String comments;
public static GLClosure createNew(final Office office, final Date closingDate, final String comments) {
return new GLClosure(office, closingDate, comments);
}
protected GLClosure() { protected GLClosure() {
// //
} }
@@ -59,11 +59,6 @@ public class GLAccount extends AbstractPersistable<Long> {
@Column(name = "description", nullable = true, length = 500) @Column(name = "description", nullable = true, length = 500)
private String description; 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() { 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); GLAccountData retrieveGLAccountById(long glAccountId);
List<GLAccountData> retrieveAllEnabledDetailGLAccounts();
List<GLAccountData> retrieveAllEnabledDetailGLAccounts(GLAccountType accountType); List<GLAccountData> retrieveAllEnabledDetailGLAccounts(GLAccountType accountType);
GLAccountData retrieveNewGLAccountDetails(); GLAccountData retrieveNewGLAccountDetails();
} }
@@ -166,6 +166,11 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe
return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.DETAIL.getValue(), null, false); 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) { private static boolean checkValidGLAccountType(final int type) {
for (final GLAccountType accountType : GLAccountType.values()) { for (final GLAccountType accountType : GLAccountType.values()) {
if (accountType.getValue().equals(type)) { return true; } if (accountType.getValue().equals(type)) { return true; }
@@ -56,7 +56,7 @@ public class JournalEntry extends AbstractAuditableCustom<AppUser, Long> {
@Temporal(TemporalType.DATE) @Temporal(TemporalType.DATE)
private Date transactionDate; private Date transactionDate;
@Column(name = "type_enum", nullable = false, length = 50) @Column(name = "type_enum", nullable = false)
private Integer type; private Integer type;
@Column(name = "amount", scale = 6, precision = 19, nullable = false) @Column(name = "amount", scale = 6, precision = 19, nullable = false)
@@ -44,4 +44,12 @@ public enum PortfolioProductType {
return type; 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"; this.href = "/centers/" + centerId + "?command=activate";
return this; 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); 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, private ChargeData(final Long id, final String name, final BigDecimal amount, final CurrencyData currency,
final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType, final EnumOptionData chargeTimeType, final EnumOptionData chargeAppliesTo, final EnumOptionData chargeCalculationType,
final boolean penalty, final boolean active, final Collection<CurrencyData> currencyOptions, final boolean penalty, final boolean active, final Collection<CurrencyData> currencyOptions,