diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java new file mode 100644 index 000000000..94e0a537d --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/data/GLAccountDataForLookup.java @@ -0,0 +1,15 @@ +package org.mifosplatform.accounting.glaccount.data; + +public class GLAccountDataForLookup { + + private final Long id; + + public GLAccountDataForLookup(final Long id) { + this.id = id; + } + + public Long getId() { + return this.id; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java index d982c3153..c03ba7423 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformService.java @@ -8,6 +8,7 @@ package org.mifosplatform.accounting.glaccount.service; import java.util.List; import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup; import org.mifosplatform.accounting.glaccount.domain.GLAccountType; public interface GLAccountReadPlatformService { @@ -24,4 +25,6 @@ public interface GLAccountReadPlatformService { List retrieveAllEnabledHeaderGLAccounts(GLAccountType accountType); GLAccountData retrieveNewGLAccountDetails(final Integer type); + + List retrieveAccountsByTagId(final Long ruleId, final Integer transactionType); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java index 1380a34bc..5f85a5c77 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/glaccount/service/GLAccountReadPlatformServiceImpl.java @@ -13,6 +13,7 @@ import java.util.List; import org.apache.commons.lang.StringUtils; import org.mifosplatform.accounting.common.AccountingEnumerations; import org.mifosplatform.accounting.glaccount.data.GLAccountData; +import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup; import org.mifosplatform.accounting.glaccount.domain.GLAccountType; import org.mifosplatform.accounting.glaccount.domain.GLAccountUsage; import org.mifosplatform.accounting.glaccount.exception.GLAccountInvalidClassificationException; @@ -65,7 +66,7 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe final String nameDecorated = rs.getString("nameDecorated"); final Long codeId = rs.wasNull() ? null : rs.getLong("codeId"); final String codeValue = rs.getString("codeValue"); - CodeValueData tagId = CodeValueData.instance(codeId, codeValue); + final CodeValueData tagId = CodeValueData.instance(codeId, codeValue); return new GLAccountData(id, name, parentId, glCode, disabled, manualEntriesAllowed, accountType, usage, description, nameDecorated, tagId); } @@ -202,4 +203,25 @@ public class GLAccountReadPlatformServiceImpl implements GLAccountReadPlatformSe public List retrieveAllEnabledHeaderGLAccounts(final GLAccountType accountType) { return retrieveAllGLAccounts(accountType.getValue(), null, GLAccountUsage.HEADER.getValue(), null, false); } + + @Override + public List retrieveAccountsByTagId(final Long ruleId, final Integer transactionType) { + final GLAccountDataLookUpMapper mapper = new GLAccountDataLookUpMapper(); + final String sql = "Select " + mapper.schema() + " where rule.id=? and tags.acc_type_enum=?"; + return this.jdbcTemplate.query(sql, mapper, new Object[] { ruleId, transactionType }); + } + + private static final class GLAccountDataLookUpMapper implements RowMapper { + + public String schema() { + return " gl.id as id from acc_accounting_rule rule join acc_rule_tags tags on tags.acc_rule_id = rule.id join acc_gl_account gl on gl.tag_id=tags.tag_id"; + } + + @Override + public GLAccountDataForLookup mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + final Long id = JdbcSupport.getLong(rs, "id"); + return new GLAccountDataForLookup(id); + } + + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/command/JournalEntryCommand.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/command/JournalEntryCommand.java index 22e45080b..2c7eab279 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/command/JournalEntryCommand.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/command/JournalEntryCommand.java @@ -30,8 +30,8 @@ public class JournalEntryCommand { 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 Long accountingRuleId, final BigDecimal amount) { + final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits, final String referenceNumber, + final Long accountingRuleId, final BigDecimal amount) { this.officeId = officeId; this.transactionDate = transactionDate; this.comments = comments; @@ -53,46 +53,42 @@ 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("referenceNumber").value(this.referenceNumber).ignoreIfNull().notExceedingLengthOf(100); - + baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).ignoreIfNull().longGreaterThanZero(); - - if (this.accountingRuleId == null) { - - 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++; - } + + baseDataValidator.reset().parameter("credits").value(this.credits).ignoreIfNull(); + + baseDataValidator.reset().parameter("debits").value(this.debits).ignoreIfNull(); + + // 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++; - } - } - } - } else { - baseDataValidator.reset().parameter("amount").value(this.amount).notNull().zeroOrPositiveAmount(); } + // 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++; + } + } + } + baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().zeroOrPositiveAmount(); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } } @@ -134,8 +130,8 @@ public class JournalEntryCommand { return this.referenceNumber; } - public Long getAccountingRuleId() { - return this.accountingRuleId; - } + public Long getAccountingRuleId() { + return this.accountingRuleId; + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/exception/JournalEntryInvalidException.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/exception/JournalEntryInvalidException.java index 187a2b395..f3c38462b 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/exception/JournalEntryInvalidException.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/exception/JournalEntryInvalidException.java @@ -16,7 +16,7 @@ public class JournalEntryInvalidException extends AbstractPlatformDomainRuleExce /*** enum of reasons for invalid Journal Entry **/ public static enum GL_JOURNAL_ENTRY_INVALID_REASON { - FUTURE_DATE, ACCOUNTING_CLOSED, NO_DEBITS_OR_CREDITS, DEBIT_CREDIT_SUM_MISMATCH, DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, GL_ACCOUNT_DISABLED, GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED; + FUTURE_DATE, ACCOUNTING_CLOSED, NO_DEBITS_OR_CREDITS, DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT, DEBIT_CREDIT_SUM_MISMATCH, DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, GL_ACCOUNT_DISABLED, GL_ACCOUNT_MANUAL_ENTRIES_NOT_PERMITTED; public String errorMessage() { if (name().toString().equalsIgnoreCase("FUTURE_DATE")) { @@ -25,6 +25,8 @@ public class JournalEntryInvalidException extends AbstractPlatformDomainRuleExce return "Journal entry cannot be made prior to last account closing date for the branch"; } else if (name().toString().equalsIgnoreCase("NO_DEBITS_OR_CREDITS")) { return "Journal Entry must have atleast one Debit and one Credit"; + } else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT")) { + return "Sum of All Debits OR Credits must equal the Amount for a Journal Entry"; } else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_SUM_MISMATCH")) { return "Sum of All Debits must equal the sum of all Credits for a Journal Entry"; } else if (name().toString().equalsIgnoreCase("DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY")) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java index dd3e63383..e246e3471 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java @@ -6,6 +6,7 @@ package org.mifosplatform.accounting.journalentry.service; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; @@ -15,9 +16,11 @@ import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.mifosplatform.accounting.closure.domain.GLClosure; import org.mifosplatform.accounting.closure.domain.GLClosureRepository; +import org.mifosplatform.accounting.glaccount.data.GLAccountDataForLookup; import org.mifosplatform.accounting.glaccount.domain.GLAccount; import org.mifosplatform.accounting.glaccount.domain.GLAccountRepository; import org.mifosplatform.accounting.glaccount.exception.GLAccountNotFoundException; +import org.mifosplatform.accounting.glaccount.service.GLAccountReadPlatformService; import org.mifosplatform.accounting.journalentry.api.JournalEntryJsonInputParams; import org.mifosplatform.accounting.journalentry.command.JournalEntryCommand; import org.mifosplatform.accounting.journalentry.command.SingleDebitOrCreditEntryCommand; @@ -61,6 +64,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa private final AccountingProcessorHelper helper; private final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final AccountingRuleRepository accountingRuleRepository; + private final GLAccountReadPlatformService glAccountReadPlatformService; @Autowired public JournalEntryWritePlatformServiceJpaRepositoryImpl(final GLClosureRepository glClosureRepository, @@ -68,7 +72,8 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa final GLAccountRepository glAccountRepository, final JournalEntryCommandFromApiJsonDeserializer fromApiJsonDeserializer, final AccountingProcessorHelper accountingProcessorHelper, final AccountingRuleRepository accountingRuleRepository, final AccountingProcessorForLoanFactory accountingProcessorForLoanFactory, - final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory) { + final AccountingProcessorForSavingsFactory accountingProcessorForSavingsFactory, + final GLAccountReadPlatformService glAccountReadPlatformService) { this.glClosureRepository = glClosureRepository; this.officeRepository = officeRepository; this.glJournalEntryRepository = glJournalEntryRepository; @@ -78,6 +83,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa this.accountingProcessorForSavingsFactory = accountingProcessorForSavingsFactory; this.helper = accountingProcessorHelper; this.accountingRuleRepository = accountingRuleRepository; + this.glAccountReadPlatformService = glAccountReadPlatformService; } @Transactional @@ -99,32 +105,65 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa /** 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 String referenceNumber = command.stringValueOfParameterNamed(JournalEntryJsonInputParams.REFERENCE_NUMBER.getValue()); final boolean manualEntry = true; if (accountRuleId != null) { - - 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 = null; 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); + final AccountingRule accountingRule = this.accountingRuleRepository.findOne(accountRuleId); + if (accountingRule == null) { throw new AccountingRuleNotFoundException(accountRuleId); } - JournalEntry debitEntry = JournalEntry.createNew(office, debitAccountHead, transactionId, manualEntry, transactionDate, - JournalEntryType.DEBIT, amount, description, null, null, referenceNumber); - this.glJournalEntryRepository.saveAndFlush(debitEntry); + if (accountingRule.getAccountToCredit() == null) { + if (journalEntryCommand.getCredits() == null) { throw new RuntimeException( + "You have to pass atleast one value for credits"); } + if (journalEntryCommand.getDebits() != null) { + checkDebitOrCreditAccountsAreValid(accountingRule, journalEntryCommand.getCredits(), + journalEntryCommand.getDebits()); + checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), journalEntryCommand.getDebits()); + } else { + checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), amount); + } + saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getCredits(), + transactionId, JournalEntryType.CREDIT, referenceNumber); + } else { + if (journalEntryCommand.getCredits() != null) { throw new RuntimeException( + "You can not pass credits for this rule please verify your rule"); } + final GLAccount creditAccountHead = accountingRule.getAccountToCredit(); + validateGLAccountForTransaction(creditAccountHead); + final JournalEntry creditEntry = JournalEntry.createNew(office, creditAccountHead, transactionId, manualEntry, + transactionDate, JournalEntryType.CREDIT, amount, description, null, null, referenceNumber); + this.glJournalEntryRepository.saveAndFlush(creditEntry); + } + + if (accountingRule.getAccountToDebit() == null) { + if (journalEntryCommand.getDebits() == null) { throw new RuntimeException( + "You have to pass atleast one parameter for debits"); } + if (journalEntryCommand.getCredits() != null) { + checkDebitOrCreditAccountsAreValid(accountingRule, journalEntryCommand.getCredits(), + journalEntryCommand.getDebits()); + checkDebitAndCreditAmounts(journalEntryCommand.getCredits(), journalEntryCommand.getDebits()); + } else { + checkDebitAndCreditAmounts(journalEntryCommand.getDebits(), amount); + } + + saveAllDebitOrCreditEntries(journalEntryCommand, office, transactionDate, journalEntryCommand.getDebits(), + transactionId, JournalEntryType.DEBIT, referenceNumber); + } else { + if (journalEntryCommand.getDebits() != null) { throw new RuntimeException( + "You can not pass debits for this rule please verify your rule"); } + final GLAccount debitAccountHead = accountingRule.getAccountToDebit(); + validateGLAccountForTransaction(debitAccountHead); + final 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.getDebits(), transactionId, @@ -143,7 +182,76 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa } } - private void validateGLAccountForTransaction(GLAccount creditOrDebitAccountHead) { + @SuppressWarnings("null") + private void checkDebitOrCreditAccountsAreValid(final AccountingRule accountingRule, final SingleDebitOrCreditEntryCommand[] credits, + final SingleDebitOrCreditEntryCommand[] debits) { + // Validate the debit and credit arrays are appropriate accounts + List allowedCreditGLAccounts = new ArrayList(); + List allowedDebitGLAccounts = new ArrayList(); + final SingleDebitOrCreditEntryCommand[] validCredits = new SingleDebitOrCreditEntryCommand[credits.length]; + final SingleDebitOrCreditEntryCommand[] validDebits = new SingleDebitOrCreditEntryCommand[debits.length]; + + if (credits != null && credits.length > 0) { + allowedCreditGLAccounts = this.glAccountReadPlatformService.retrieveAccountsByTagId(accountingRule.getId(), + JournalEntryType.CREDIT.getValue()); + for (final GLAccountDataForLookup accountDataForLookup : allowedCreditGLAccounts) { + for (int i = 0; i < credits.length; i++) { + final SingleDebitOrCreditEntryCommand credit = credits[i]; + if (credit.getGlAccountId().equals(accountDataForLookup.getId())) { + validCredits[i] = credit; + } + } + } + if (credits.length != validCredits.length) { throw new RuntimeException("Invalid credits"); } + } + + if (debits != null && debits.length > 0) { + allowedDebitGLAccounts = this.glAccountReadPlatformService.retrieveAccountsByTagId(accountingRule.getId(), + JournalEntryType.DEBIT.getValue()); + for (final GLAccountDataForLookup accountDataForLookup : allowedDebitGLAccounts) { + for (int i = 0; i < debits.length; i++) { + final SingleDebitOrCreditEntryCommand debit = debits[i]; + if (debit.getGlAccountId().equals(accountDataForLookup.getId())) { + validDebits[i] = debit; + } + } + } + if (debits.length != validDebits.length) { throw new RuntimeException("Invalid debits"); } + } + } + + private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] credits, final SingleDebitOrCreditEntryCommand[] debits) { + // 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); } + } + + private void checkDebitAndCreditAmounts(final SingleDebitOrCreditEntryCommand[] creditOrDebits, final BigDecimal amount) { + if (amount == null) { throw new JournalEntryInvalidException(GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH_WITH_AMOUNT, + null, null, null); } + BigDecimal creditOrDebitSum = BigDecimal.ZERO; + for (final SingleDebitOrCreditEntryCommand creditEntryCommand : creditOrDebits) { + if (creditEntryCommand.getAmount() == null || creditEntryCommand.getGlAccountId() == null) { throw new JournalEntryInvalidException( + GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_ACCOUNT_OR_AMOUNT_EMPTY, null, null, null); } + creditOrDebitSum = creditOrDebitSum.add(creditEntryCommand.getAmount()); + } + if (creditOrDebitSum.compareTo(amount) != 0) { throw new JournalEntryInvalidException( + GL_JOURNAL_ENTRY_INVALID_REASON.DEBIT_CREDIT_SUM_MISMATCH, null, null, null); } + } + + private void validateGLAccountForTransaction(final GLAccount creditOrDebitAccountHead) { /*** * validate that the account allows manual adjustments and is not * disabled @@ -199,7 +307,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled"); if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) { - final LoanDTO loanDTO = helper.populateLoanDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled, + final LoanDTO loanDTO = this.helper.populateLoanDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled, accrualBasedAccountingEnabled); final AccountingProcessorForLoan accountingProcessorForLoan = this.accountingProcessorForLoanFactory .determineProcessor(loanDTO); @@ -209,13 +317,13 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa @Transactional @Override - public void createJournalEntriesForSavings(Map accountingBridgeData) { + public void createJournalEntriesForSavings(final Map accountingBridgeData) { final boolean cashBasedAccountingEnabled = (Boolean) accountingBridgeData.get("cashBasedAccountingEnabled"); final boolean accrualBasedAccountingEnabled = (Boolean) accountingBridgeData.get("accrualBasedAccountingEnabled"); if (cashBasedAccountingEnabled || accrualBasedAccountingEnabled) { - final SavingsDTO savingsDTO = helper.populateSavingsDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled, + final SavingsDTO savingsDTO = this.helper.populateSavingsDtoFromMap(accountingBridgeData, cashBasedAccountingEnabled, accrualBasedAccountingEnabled); final AccountingProcessorForSavings accountingProcessorForSavings = this.accountingProcessorForSavingsFactory .determineProcessor(savingsDTO); @@ -247,21 +355,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa 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); } + checkDebitAndCreditAmounts(credits, debits); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java index 82603f1f3..cdc845b28 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/domain/AccountingTagRule.java @@ -24,10 +24,10 @@ public class AccountingTagRule extends AbstractPersistable { @ManyToOne @JoinColumn(name = "tag_id", nullable = false) - private final CodeValue tagId; + private CodeValue tagId; @Column(name = "acc_type_enum", nullable = false) - private final Integer accountType; + private Integer accountType; public static AccountingTagRule create(final CodeValue tagId, final Integer accountType) { return new AccountingTagRule(tagId, accountType); @@ -42,4 +42,12 @@ public class AccountingTagRule extends AbstractPersistable { this.accountingRule = accountingRule; } + public AccountingTagRule() { + // TODO Auto-generated constructor stub + } + + public Integer getAccountType() { + return this.accountType; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java index 2e38b7516..483b8a6a2 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/rule/service/AccountingRuleWritePlatformServiceJpaRepositoryImpl.java @@ -21,6 +21,7 @@ import org.mifosplatform.accounting.rule.domain.AccountingTagRule; import org.mifosplatform.accounting.rule.exception.AccountingRuleDuplicateException; import org.mifosplatform.infrastructure.codes.domain.CodeValue; import org.mifosplatform.infrastructure.codes.domain.CodeValueRepository; +import org.mifosplatform.infrastructure.codes.exception.CodeValueNotFoundException; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; @@ -109,20 +110,22 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco GLAccount creditAccount = null; List accountingTagRules = new ArrayList(); - if (accountToDebitId != null) { + if ((accountToDebitId != null && debitTags != null) || (accountToDebitId == null && debitTags == null)) { + // TODO- Throw Appropriate exception + throw new RuntimeException("Please Specify debitTags or accountToDebitId"); + } else if (accountToDebitId != null) { debitAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId); } else if (debitTags != null) { accountingTagRules = saveDebitOrCreditTags(debitTags, JournalEntryType.DEBIT, accountingTagRules); - } else { - throw new RuntimeException("Please Specify debitTags or accountToDebitId"); } - if (accountToCreditId != null) { - creditAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId); - } else if (debitTags != null) { - accountingTagRules = saveDebitOrCreditTags(creditTags, JournalEntryType.CREDIT, accountingTagRules); - } else { + if ((accountToCreditId != null && creditTags != null) || (accountToCreditId == null && creditTags == null)) { + // TODO- Throw Appropriate exception throw new RuntimeException("Please Specify debitTags or accountToDebitId"); + } else if (accountToCreditId != null) { + creditAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId); + } else if (creditTags != null) { + accountingTagRules = saveDebitOrCreditTags(creditTags, JournalEntryType.CREDIT, accountingTagRules); } final AccountingRule accountingRule = AccountingRule.fromJson(office, debitAccount, creditAccount, command); @@ -131,6 +134,8 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco return accountingRule; } + // TODO- This update mehtod should update based on new changes made for + // createAccountingRule. @Transactional @Override public CommandProcessingResult updateAccountingRule(final Long accountingRuleId, final JsonCommand command) { @@ -205,6 +210,7 @@ public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements Acco for (final String creditOrDebitTag : creditOrDebitTagArray) { final Long creditOrDebitTagIdLongValue = Long.valueOf(creditOrDebitTag); final CodeValue creditOrDebitAccount = this.codeValueRepository.findOne(creditOrDebitTagIdLongValue); + if (creditOrDebitAccount == null) { throw new CodeValueNotFoundException(creditOrDebitTagIdLongValue); } final AccountingTagRule accountingTagRule = AccountingTagRule.create(creditOrDebitAccount, transactionType.getValue()); accountingTagRules.add(accountingTagRule); }