Making JournalEntries With predefined accounting rule

Fixing issues with constructor for journalEntryCommand
This commit is contained in:
mifoscontributer
2013-05-22 12:46:00 +05:30
parent 418790f59e
commit c896e3d1bd
5 changed files with 152 additions and 69 deletions
@@ -13,7 +13,7 @@ import java.util.Set;
***/
public enum JournalEntryJsonInputParams {
OFFICE_ID("officeId"), TRANSACTION_DATE("transactionDate"), COMMENTS("comments"), CREDITS("credits"), DEBITS("debits"), LOCALE("locale"), DATE_FORMAT(
"dateFormat"), REFERENCE_NUMBER("referenceNumber") ;
"dateFormat"), REFERENCE_NUMBER("referenceNumber"),IS_PREDEFINED_RULE_ENTRY("isPredefinedRuleEntry"),ACCOUNTING_RULE("accountingRule"), AMOUNT("amount");
private final String value;
@@ -5,6 +5,7 @@
*/
package org.mifosplatform.accounting.journalentry.command;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@@ -22,18 +23,35 @@ public class JournalEntryCommand {
private final LocalDate transactionDate;
private final String comments;
private final String referenceNumber;
private final Boolean isPredefinedRuleEntry;
private final Long accountingRuleId;
private final BigDecimal amount;
private final SingleDebitOrCreditEntryCommand[] credits;
private final SingleDebitOrCreditEntryCommand[] debits;
public JournalEntryCommand(final Long officeId, final LocalDate transactionDate, final String comments,
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits, final String referenceNumber) {
final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits,
final String referenceNumber, final Boolean isPredefinedRuleEntry, final Long accountingRuleId, final BigDecimal amount) {
this.officeId = officeId;
this.transactionDate = transactionDate;
this.comments = comments;
this.credits = credits;
this.debits = debits;
this.referenceNumber = referenceNumber;
this.isPredefinedRuleEntry = isPredefinedRuleEntry;
this.accountingRuleId = accountingRuleId;
this.amount = amount;
}
public JournalEntryCommand(final Long officeId, final LocalDate transactionDate, final String comments, final String referenceNumber,
final Boolean isPredefinedRuleEntry, final Long accountingRuleId, final BigDecimal amount) {
this(officeId, transactionDate, comments, null, null, referenceNumber, isPredefinedRuleEntry, accountingRuleId, amount);
}
public JournalEntryCommand(Long officeId, LocalDate transactionDate, String comments, SingleDebitOrCreditEntryCommand[] credits,
SingleDebitOrCreditEntryCommand[] debits, String referenceNumber, Boolean isPredefinedRuleEntry) {
this(officeId, transactionDate, comments, credits, debits, referenceNumber, isPredefinedRuleEntry, null,null);
}
public void validateForCreate() {
@@ -47,37 +65,45 @@ public class JournalEntryCommand {
baseDataValidator.reset().parameter("officeId").value(this.officeId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("comments").value(this.comments).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("credits").value(this.credits).notNull();
baseDataValidator.reset().parameter("debits").value(this.debits).notNull();
baseDataValidator.reset().parameter("isPredefinedRuleEntry").value(this.isPredefinedRuleEntry).notNull();
baseDataValidator.reset().parameter("referenceNumber").value(this.referenceNumber).ignoreIfNull().notExceedingLengthOf(100);
// validation for credit array elements
if (this.credits != null) {
if (this.credits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
i++;
if (!this.isPredefinedRuleEntry) {
baseDataValidator.reset().parameter("credits").value(this.credits).notNull();
baseDataValidator.reset().parameter("debits").value(this.debits).notNull();
// validation for credit array elements
if (this.credits != null) {
if (this.credits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
i++;
}
}
}
}
// validation for debit array elements
if (this.debits != null) {
if (this.debits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
i++;
// validation for debit array elements
if (this.debits != null) {
if (this.debits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
i++;
}
}
}
} else {
baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).notNull().longGreaterThanZero();
baseDataValidator.reset().parameter("amount").value(this.amount).notNull().zeroOrPositiveAmount();
}
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
@@ -121,4 +147,8 @@ public class JournalEntryCommand {
return this.referenceNumber;
}
public Boolean getIsPredefinedRuleEntry() {
return this.isPredefinedRuleEntry;
}
}
@@ -59,10 +59,17 @@ public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFr
JournalEntryJsonInputParams.TRANSACTION_DATE.getValue(), element);
final String referenceNumber = this.fromApiJsonHelper.extractStringNamed(
JournalEntryJsonInputParams.REFERENCE_NUMBER.getValue(), element);
final Boolean isPredefinedRuleEntry = this.fromApiJsonHelper.extractBooleanNamed(JournalEntryJsonInputParams.IS_PREDEFINED_RULE_ENTRY.getValue(), element);
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
if(isPredefinedRuleEntry) {
final Long accountingRuleId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.ACCOUNTING_RULE.getValue(), element);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(JournalEntryJsonInputParams.AMOUNT.getValue(), element, locale);
return new JournalEntryCommand(officeId, transactionDate, comments, referenceNumber, isPredefinedRuleEntry, accountingRuleId, amount);
}
SingleDebitOrCreditEntryCommand[] credits = null;
SingleDebitOrCreditEntryCommand[] debits = null;
if (element.isJsonObject()) {
@@ -75,7 +82,7 @@ public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFr
debits = populateCreditsOrDebitsArray(topLevelJsonElement, locale, debits, JournalEntryJsonInputParams.DEBITS.getValue());
}
}
return new JournalEntryCommand(officeId, transactionDate, comments, credits, debits,referenceNumber);
return new JournalEntryCommand(officeId, transactionDate, comments, credits, debits, referenceNumber, isPredefinedRuleEntry);
}
/**
@@ -29,6 +29,9 @@ import org.mifosplatform.accounting.journalentry.exception.JournalEntriesNotFoun
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException;
import org.mifosplatform.accounting.journalentry.exception.JournalEntryInvalidException.GL_JOURNAL_ENTRY_INVALID_REASON;
import org.mifosplatform.accounting.journalentry.serialization.JournalEntryCommandFromApiJsonDeserializer;
import org.mifosplatform.accounting.rule.domain.AccountingRule;
import org.mifosplatform.accounting.rule.domain.AccountingRuleRepository;
import org.mifosplatform.accounting.rule.exception.AccountingRuleNotFoundException;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -55,12 +58,13 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
private final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory;
private final AccountingProcessorHelper helper;
private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final AccountingRuleRepository accountingRuleRepository;
@Autowired
public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository,
final JournalEntryRepository glJournalEntryRepository, final OfficeRepository officeRepository,
final GLAccountRepository glAccountRepository, final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer,
final AccountingProcessorHelper accountingProcessorHelper,
final AccountingProcessorHelper accountingProcessorHelper, final AccountingRuleRepository accountingRuleRepository,
final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory) {
this.glClosureRepository = glClosureRepository;
this.officeRepository = officeRepository;
@@ -69,6 +73,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
this.glAccountRepository = glAccountRepository;
this.accountingProcessorForLoanFactory = accountingProcessorForLoanFactory;
this.helper = accountingProcessorHelper;
this.accountingRuleRepository = accountingRuleRepository;
}
@Transactional
@@ -82,18 +87,50 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final Long officeId = command.longValueOfParameterNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue());
final Office office = this.officeRepository.findOne(officeId);
if (office == null) { throw new OfficeNotFoundException(officeId); }
final boolean isPredefinedRuleEntry = command.booleanPrimitiveValueOfParameterNamed(JournalEntryJsonInputParams.IS_PREDEFINED_RULE_ENTRY.getValue());
validateBusinessRulesForJournalEntries(journalEntryCommand);
/** Set a transaction Id and save these Journal entries **/
final Date transactionDate = command.DateValueOfParameterNamed(JournalEntryJsonInputParams.TRANSACTION_DATE.getValue());
final String transactionId = generateTransactionId();
final String referenceNumber = journalEntryCommand.getReferenceNumber();
final boolean manualEntry = true;
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(), transactionId,
JournalEntryType.DEBIT);
if (isPredefinedRuleEntry) {
final Long accountingRuleId = command.longValueOfParameterNamed(JournalEntryJsonInputParams.ACCOUNTING_RULE.getValue());
final BigDecimal amount = command.bigDecimalValueOfParameterNamed(JournalEntryJsonInputParams.AMOUNT.getValue());
final AccountingRule accountingRule = this.accountingRuleRepository.findOne(accountingRuleId);
if (accountingRule == null) { throw new AccountingRuleNotFoundException(accountingRuleId); }
final GLAccount creditAccountHead = accountingRule.getAccountToCredit();
validateGLAccountForTransaction(creditAccountHead);
final GLAccount debitAccountHead = accountingRule.getAccountToDebit();
validateGLAccountForTransaction(debitAccountHead);
String description = accountingRule.getDescription();
if (StringUtils.isNotBlank(journalEntryCommand.getComments())) {
description = journalEntryCommand.getComments();
}
JournalEntry creditEntry = JournalEntry.createNew(office, creditAccountHead, transactionId, manualEntry, transactionDate,
JournalEntryType.CREDIT, amount, description, null, null, referenceNumber);
this.glJournalEntryRepository.saveAndFlush(creditEntry);
JournalEntry debitEntry = JournalEntry.createNew(office, debitAccountHead, transactionId, manualEntry, transactionDate,
JournalEntryType.DEBIT, amount, description, null, null, referenceNumber);
this.glJournalEntryRepository.saveAndFlush(debitEntry);
} else {
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getCredits(), transactionId,
JournalEntryType.CREDIT);
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(), transactionId,
JournalEntryType.DEBIT, referenceNumber);
saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getCredits(), transactionId,
JournalEntryType.CREDIT, referenceNumber);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withTransactionId(transactionId).build();
@@ -103,6 +140,19 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
}
}
private void validateGLAccountForTransaction(GLAccount creditOrDebitAccountHead) {
/***
* validate that the account allows manual adjustments and is not
* disabled
**/
if (creditOrDebitAccountHead.isDisabled()) {
throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_DISABLED, null, creditOrDebitAccountHead.getName(),
creditOrDebitAccountHead.getGlCode());
} else if (!creditOrDebitAccountHead.isManualEntriesAllowed()) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED, null, creditOrDebitAccountHead.getName(),
creditOrDebitAccountHead.getGlCode()); }
}
@Transactional
@Override
public CommandProcessingResult revertJournalEntry(final JsonCommand command) {
@@ -168,56 +218,48 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
if (latestGLClosure.getClosingDate().after(transactionDate) || latestGLClosure.getClosingDate().equals(transactionDate)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), null, null); }
}
/*** check if credits and debits are valid **/
final SingleDebitOrCreditEntryCommand[] credits = command.getCredits();
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
// sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO;
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
if(!command.getIsPredefinedRuleEntry()) {
/*** check if credits and debits are valid **/
final SingleDebitOrCreditEntryCommand[] credits = command.getCredits();
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
// atleast one debit or credit must be present
if ((credits == null || credits.length <= 0) || (debits == null || debits.length <= 0)) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.NO_DEBITS_OR_CREDITS, null, null, null); }
// sum of all debits must be = sum of all credits
BigDecimal creditsSum = BigDecimal.ZERO;
BigDecimal debitsSum = BigDecimal.ZERO;
for (final SingleDebitOrCreditEntryCommand creditEntryCommand : credits) {
if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
creditsSum = creditsSum.add(creditEntryCommand.getAmount());
}
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
}
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
}
for (final SingleDebitOrCreditEntryCommand debitEntryCommand : debits) {
if (debitEntryCommand.getAmount() == null || debitEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); }
debitsSum = debitsSum.add(debitEntryCommand.getAmount());
}
if (creditsSum.compareTo(debitsSum) != 0) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); }
}
private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, final Office office, final Date transactionDate,
final SingleDebitOrCreditEntryCommand[] singleDebitOrCreditEntryCommands, final String transactionId,
final JournalEntryType type) {
final JournalEntryType type, final String referenceNumber) {
final boolean manualEntry = true;
for (final SingleDebitOrCreditEntryCommand singleDebitOrCreditEntryCommand : singleDebitOrCreditEntryCommands) {
final GLAccount glAccount = this.glAccountRepository.findOne(singleDebitOrCreditEntryCommand.getGlAccountId());
if (glAccount == null) { throw new GLAccountNotFoundException(singleDebitOrCreditEntryCommand.getGlAccountId()); }
/***
* validate that the account allows manual adjustments and is not
* disabled
**/
if (glAccount.isDisabled()) {
throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_DISABLED, null, glAccount.getName(),
glAccount.getGlCode());
} else if (!glAccount.isManualEntriesAllowed()) { throw new JournalEntryInvalidException(
GL_JOURNAL_ENTRY_INVALID_REASON.GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED, null, glAccount.getName(),
glAccount.getGlCode()); }
validateGLAccountForTransaction(glAccount);
String comments = command.getComments();
if (!StringUtils.isBlank(singleDebitOrCreditEntryCommand.getComments())) {
comments = singleDebitOrCreditEntryCommand.getComments();
}
String referenceNumber = command.getReferenceNumber();
final JournalEntry glJournalEntry = JournalEntry.createNew(office, glAccount, transactionId, manualEntry, transactionDate,
type, singleDebitOrCreditEntryCommand.getAmount(), comments, null, null, referenceNumber);
this.glJournalEntryRepository.saveAndFlush(glJournalEntry);
@@ -147,4 +147,8 @@ public class AccountingRule extends AbstractPersistable<Long> {
this.accountToCredit = accountToCredit;
}
public String getDescription() {
return this.description;
}
}