You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by GitBox <gi...@apache.org> on 2020/05/02 16:57:30 UTC

[GitHub] [fineract] maektwain opened a new pull request #796: FINERACT-38

maektwain opened a new pull request #796:
URL: https://github.com/apache/fineract/pull/796


   Test Integrated
   
   Improved Tests
   
   Adding PreviewIncomeAndExpense
   
   ## Description
   Describe the changes made and why they were made. Ignore if these details are present on the associated Jira ticket
   
   ## Checklist
   Please make sure these boxes are checked before submitting your pull request - thanks!
   
   - [ ] Commit message starts with the issue number from https://issues.apache.org/jira/projects/FINERACT/. Ex: FINERACT-646 Pockets API.
   
   - [ ] Coding conventions at https://cwiki.apache.org/confluence/display/FINERACT/Coding+Conventions have been followed.
   
   - [ ] API documentation at https://github.com/apache/fineract/blob/develop/api-docs/apiLive.htm has been updated with details of any API changes.
   
   - [ ] Integration tests have been created/updated for verifying the changes made.
   
   - [ ] All Integrations tests are passing with the new commits.
   
   - [ ] Submission is not a "code dump".  (Large changes can be made "in repository" via a branch.  Ask on the list.)
   
   Our guidelines for code reviews is at https://cwiki.apache.org/confluence/display/FINERACT/Code+Review+Guide
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-622990254


   @xurror could you see and get this reviewed ?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-623071405


   @vorburger This test failed 
   
   ```
   org.apache.fineract.integrationtests.SchedulerJobsTestResults > testApplyHolidaysToLoansJobOutcome FAILED
   8245    java.lang.AssertionError at SchedulerJobsTestResults.java:326
   ```


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r420969966



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/CalculateIncomeAndExpenseBookingImpl.java
##########
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.accounting.closure.service;
+
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.data.JournalEntryData;
+import org.apache.fineract.accounting.closure.data.SingleDebitOrCreditEntryData;
+import org.apache.fineract.accounting.closure.domain.GLClosure;
+import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
+import org.apache.fineract.accounting.closure.exception.GLClosureInvalidException;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer;
+import org.apache.fineract.accounting.glaccount.domain.GLAccount;
+import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
+import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
+import org.apache.fineract.infrastructure.core.api.JsonQuery;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.office.domain.Office;
+import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
+import org.apache.fineract.organisation.office.exception.OfficeNotFoundException;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+@Service
+public class CalculateIncomeAndExpenseBookingImpl implements CalculateIncomeAndExpenseBooking {
+
+    private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+    private final OfficeRepositoryWrapper officeRepository;
+    private final GLClosureRepository glClosureRepository;
+    private final GLAccountRepositoryWrapper glAccountRepository;
+    private final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private final OfficeReadPlatformService officeReadPlatformService;
+
+    @Autowired
+    public CalculateIncomeAndExpenseBookingImpl(final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer,
+            final OfficeRepositoryWrapper officeRepository,final GLClosureRepository glClosureRepository,
+            final GLAccountRepositoryWrapper glAccountRepository,final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService,
+            final OfficeReadPlatformService officeReadPlatformService) {
+        this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+        this.officeRepository = officeRepository;
+        this.glClosureRepository = glClosureRepository;
+        this.glAccountRepository = glAccountRepository;
+        this.incomeAndExpenseReadPlatformService = incomeAndExpenseReadPlatformService;
+        this.officeReadPlatformService = officeReadPlatformService;
+    }
+
+    @Override
+    public Collection<IncomeAndExpenseBookingData> CalculateIncomeAndExpenseBookings(JsonQuery query) {
+        final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(query.json());
+        closureCommand.validateForCreate();
+        final Long officeId = closureCommand.getOfficeId();
+        final Office office = this.officeRepository.findOneWithNotFoundDetection(officeId);
+        if (office == null) { throw new OfficeNotFoundException(officeId); }
+
+        final Date todaysDate = new Date();
+        final Date closureDate = closureCommand.getClosingDate().toDate();
+        if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(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(
+                    GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
+        }
+        final LocalDate incomeAndExpenseBookOffDate = LocalDate.fromDateFields(closureDate);
+
+         /*get all offices underneath it valid closure date for all, get Jl for all and make bookings*/
+       // final List<Office> childOffices = office.getChildren();
+        final Collection<Long> childOfficesByHierarchy = this.officeReadPlatformService.officeByHierarchy(officeId);
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final GLClosure latestChildGlClosure = this.glClosureRepository.getLatestGLClosureByBranch(childOffice);
+                if (latestChildGlClosure != null) {
+                    if (latestChildGlClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
+                            GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestChildGlClosure.getClosingDate()); }
+                }
+            }
+        }
+
+        Collection<IncomeAndExpenseBookingData> incomeAndExpenseBookingCollection = new ArrayList<>();
+        final Long equityGlAccountId = closureCommand.getEquityGlAccountId();
+
+        final GLAccount glAccount= this.glAccountRepository.findOneWithNotFoundDetection(equityGlAccountId);
+
+        if(glAccount == null){throw new GLAccountNotFoundException(equityGlAccountId);}
+
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.
+                        retrieveAllIncomeAndExpenseJournalEntryData(childOffice, incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+                final IncomeAndExpenseBookingData incomeAndExpBookingData = this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true, glAccount,this.officeRepository.findOneWithNotFoundDetection(childOffice));
+                if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+            }
+        }else{
+            final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.retrieveAllIncomeAndExpenseJournalEntryData(officeId,incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+            final IncomeAndExpenseBookingData incomeAndExpBookingData= this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true,glAccount,office);
+            if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+        }
+        return incomeAndExpenseBookingCollection;
+    }
+
+    public IncomeAndExpenseBookingData bookOffIncomeAndExpense(final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList,
+                                                                final GLClosureCommand closureData,final boolean preview,final GLAccount glAccount,final Office office){
+        /* All running balances has to be calculated before booking off income and expense account */
+        boolean isRunningBalanceCalculated = true;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJournalEntryDataList ){
+            if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+        }
+        BigDecimal debits = BigDecimal.ZERO;
+        BigDecimal credits = BigDecimal.ZERO;
+
+        final List<SingleDebitOrCreditEntryData>  debitsJournalEntry = new ArrayList<>();
+        final List<SingleDebitOrCreditEntryData>  creditsJournalEntry = new ArrayList<>();
+
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    debits = debits.add(incomeAndExpense.getOfficeRunningBalance());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance(),null));
+                }else{
+                    credits= credits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    credits = credits.add(incomeAndExpense.getOfficeRunningBalance());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }else{
+                    debits= debits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));
+                }
+            }
+        }
+        final LocalDate today = DateUtils.getLocalDateOfTenant();
+        final int compare = debits.compareTo(credits);
+        BigDecimal difference = BigDecimal.ZERO;
+        JournalEntryData journalEntry = null;
+        if(compare == 1){
+            /* book with target gl id on the credit side */
+            difference = debits.subtract(credits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            creditsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }else if(compare == -1){
+            /* book with target gl id on the debit side*/
+            difference = credits.subtract(debits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            debitsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }
+        else if(compare == 0){
+            //throw new RunningBalanceZeroException(office.getName());

Review comment:
       You are looking at the old revision @vorburger 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r432964306



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -625,6 +622,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       Resolving because this was actually done meanwhile (@maektwain in the future please comment to let reviewer know).




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r418995676



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -625,6 +622,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       Rebase here ack.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] github-actions[bot] commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
github-actions[bot] commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-652709605


   This pull request seems to be stale.  Are you still planning to work on it?  We will automatically close it in 30 days.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain edited a comment on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain edited a comment on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-622998974


   > I have done a quick technical review, and have some minor feedback.
   > 
   > Other than that, someone else should probably do a more functional review?
   
   Should I qualify as "other ", since I have done end to end functional review and testing. Though we need some "meta-other" to kind of validate "other" (Waiting)
   > 
   > We could also just merge after e.g. 2 weeks "grace period" if nobody can a functional review within 2 weeks after technical feedback is taken into account (we have done this before for other functional enhancements).
   
   I think we should write some functional tests also (We could cover such scenario, then we won't be much dependent on others to validate the functional test) then in the end only manual testing is required?
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-636816408


   Yeah , @vorburger I am planning to finish this, I need to write tests which are really crazy will be able  to finish this tomorrow 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-622998974


   > I have done a quick technical review, and have some minor feedback.
   > 
   > Other than that, someone else should probably do a more functional review?
   Should I qualify as "other ", since I have done end to end functional review and testing. Though we need some "meta-other" to kind of validate "other" (Waiting)
   > 
   > We could also just merge after e.g. 2 weeks "grace period" if nobody can a functional review within 2 weeks after technical feedback is taken into account (we have done this before for other functional enhancements).
   I think we should write some functional tests also (We could cover such scenario, then we won't be much dependent on others to validate the functional test) then in the end only manual testing is required?
   
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-626613197


   Hey @vorburger could you make this as draft, since I tested the test written they don't comply and need to re-write them again. (real integration test)


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r418993637



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -625,6 +622,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       This confused me while I was doing a technical code review of this PR, so I had a closer look at the existing code when I realized that this was just copy/pasted... it's already wrong in that existing code, I'm proposing to improve it in https://github.com/apache/fineract/pull/799.. would you be willing to rebase and change this accordingly, once that is merged?

##########
File path: fineract-provider/src/main/java/org/apache/fineract/organisation/office/service/OfficeReadPlatformServiceImpl.java
##########
@@ -266,4 +266,20 @@ public OfficeTransactionData retrieveNewOfficeTransactionDetails() {
     public PlatformSecurityContext getContext() {
         return this.context;
     }
+
+    @Override
+    public Collection<Long> officeByHierarchy(Long officeId) {
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("SELECT ounder.id FROM m_office mo ");
+        sqlBuilder.append(" JOIN m_office ounder ON ounder.hierarchy LIKE CONCAT(mo.hierarchy, '%') ");
+        sqlBuilder.append(" AND ounder.hierarchy like CONCAT('.', '%') where mo.id = ? ");
+
+
+        try {
+            return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
+                    new Object[] { officeId});
+        } catch (final EmptyResultDataAccessException e) {
+           return null;

Review comment:
       safer to return a `Collections.emptyList()` instead of `null` here 

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/api/GLClosuresApiResource.java
##########
@@ -143,6 +159,21 @@ public String createGLClosure(@ApiParam(hidden = true) final String jsonRequestB
         return this.apiJsonSerializerService.serialize(result);
     }
 
+    @POST
+    @Path("previewIncomeAndExpense")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @ApiOperation(value = "Create an End of the Year Accounting Closure")
+    public String previewIncomeAndExpenseBooking(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
+            final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
+            final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
+            final Collection<IncomeAndExpenseBookingData> incomeAndExpenseBookingCollection = this.calculateIncomeAndExpenseBooking.CalculateIncomeAndExpenseBookings(query);
+            final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+            return this.incomeAndExpenseBookingDataDefaultToApiJsonSerializer.serialize(settings, incomeAndExpenseBookingCollection, new HashSet<String>());

Review comment:
       safer to use a `Collections.emptySet()` instead of `new HashSet<String>()` here 

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/GLClosureWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -148,4 +261,99 @@ private void handleGLClosureIntegrityIssues(final JsonCommand command, final Dat
         throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
                 "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
     }
+    private void handleRunningBalanceNotCalculated(final HashMap<Long,List<IncomeAndExpenseJournalEntryData>> allIncomeAndExpenseJLWithSubBranches){
+        final Iterator<Map.Entry<Long,List<IncomeAndExpenseJournalEntryData>>> iterator = allIncomeAndExpenseJLWithSubBranches.entrySet().iterator();
+        while(iterator.hasNext()){
+            final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJL = iterator.next().getValue();
+            for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJL ){
+                if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+            }
+        }
+    }
+    private String bookOffIncomeAndExpense(final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList,
+            final GLClosureCommand closureData,final GLAccount glAccount, final Office office){
+        /* All running balances has to be calculated before booking off income and expense account */
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJournalEntryDataList ){
+            if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+        }
+        BigDecimal debits = BigDecimal.ZERO;
+        BigDecimal credits = BigDecimal.ZERO;
+
+        int i = 0;
+        int j = 0;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){debits = debits.add(incomeAndExpense.getOfficeRunningBalance());
+                i++;
+                }
+                else{ credits = credits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                j++;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){credits = credits.add(incomeAndExpense.getOfficeRunningBalance());
+                j++;
+                }
+                else{debits= debits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                i++;
+                }
+            }
+        }
+        final int compare = debits.compareTo(credits);
+        BigDecimal difference = BigDecimal.ZERO;
+        JournalEntryCommand journalEntryCommand = null;
+        if(compare ==1){ j++;}else{
+            i++;
+        }
+
+        SingleDebitOrCreditEntryCommand[]  debitsJournalEntry  = new SingleDebitOrCreditEntryCommand[i];
+        SingleDebitOrCreditEntryCommand[]  creditsJournalEntry  = new SingleDebitOrCreditEntryCommand[j];
+        int m=0;
+        int n=0;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    debitsJournalEntry[m] = new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    m++;
+                }else{
+                    creditsJournalEntry[n]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    n++;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    creditsJournalEntry[n]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    n++;
+                }else{
+                    debitsJournalEntry[m]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    m++;
+                }
+            }
+        }
+        String transactionId = null;
+        if(compare == 1){
+            /* book with target gl id on the credit side */
+            difference = debits.subtract(credits);
+            final SingleDebitOrCreditEntryCommand targetBooking = new SingleDebitOrCreditEntryCommand(null,closureData.getEquityGlAccountId(),difference,null);
+            creditsJournalEntry[n] = targetBooking;
+            journalEntryCommand = new JournalEntryCommand(office.getId(),closureData.getCurrencyCode(),closureData.getClosingDate(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,closureData.getIncomeAndExpenseComments(),
+                    null,null,null,null,null,null,null,null);
+            transactionId = this.journalEntryWritePlatformService.createJournalEntryForIncomeAndExpenseBookOff(journalEntryCommand);
+
+        }else if(compare == -1){
+            /* book with target gl id on the debit side*/
+            difference = credits.subtract(debits);
+            final SingleDebitOrCreditEntryCommand targetBooking = new SingleDebitOrCreditEntryCommand(null,closureData.getEquityGlAccountId(),difference,null);
+            debitsJournalEntry[m]= targetBooking;
+            journalEntryCommand = new JournalEntryCommand(office.getId(),closureData.getCurrencyCode(),closureData.getClosingDate(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,closureData.getIncomeAndExpenseComments(),
+                    null,null,null,null,null,null,null,null);
+            transactionId = this.journalEntryWritePlatformService.createJournalEntryForIncomeAndExpenseBookOff(journalEntryCommand);
+        }else if(compare == 0){
+            //throw new RunningBalanceZeroException(office.getName());
+            return null;

Review comment:
       This `return null`here looks very curious... won't this hide problems, are we sure that the (commented out!) throwing of an exception wouldn't be better?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-623087746


   @maektwain see https://github.com/apache/fineract#pull-requests Basically, do not wait for an active maintainer to come and help you re. build failures - it's your responsibility to get a green build. Have you done what the README suggests to do in such cases? 😺


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r420966063



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -626,6 +623,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       I rebased it , it did not push here, 🥇  

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/CalculateIncomeAndExpenseBookingImpl.java
##########
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.accounting.closure.service;
+
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.data.JournalEntryData;
+import org.apache.fineract.accounting.closure.data.SingleDebitOrCreditEntryData;
+import org.apache.fineract.accounting.closure.domain.GLClosure;
+import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
+import org.apache.fineract.accounting.closure.exception.GLClosureInvalidException;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer;
+import org.apache.fineract.accounting.glaccount.domain.GLAccount;
+import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
+import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
+import org.apache.fineract.infrastructure.core.api.JsonQuery;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.office.domain.Office;
+import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
+import org.apache.fineract.organisation.office.exception.OfficeNotFoundException;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+@Service
+public class CalculateIncomeAndExpenseBookingImpl implements CalculateIncomeAndExpenseBooking {
+
+    private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+    private final OfficeRepositoryWrapper officeRepository;
+    private final GLClosureRepository glClosureRepository;
+    private final GLAccountRepositoryWrapper glAccountRepository;
+    private final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private final OfficeReadPlatformService officeReadPlatformService;
+
+    @Autowired
+    public CalculateIncomeAndExpenseBookingImpl(final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer,
+            final OfficeRepositoryWrapper officeRepository,final GLClosureRepository glClosureRepository,
+            final GLAccountRepositoryWrapper glAccountRepository,final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService,
+            final OfficeReadPlatformService officeReadPlatformService) {
+        this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+        this.officeRepository = officeRepository;
+        this.glClosureRepository = glClosureRepository;
+        this.glAccountRepository = glAccountRepository;
+        this.incomeAndExpenseReadPlatformService = incomeAndExpenseReadPlatformService;
+        this.officeReadPlatformService = officeReadPlatformService;
+    }
+
+    @Override
+    public Collection<IncomeAndExpenseBookingData> CalculateIncomeAndExpenseBookings(JsonQuery query) {
+        final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(query.json());
+        closureCommand.validateForCreate();
+        final Long officeId = closureCommand.getOfficeId();
+        final Office office = this.officeRepository.findOneWithNotFoundDetection(officeId);
+        if (office == null) { throw new OfficeNotFoundException(officeId); }
+
+        final Date todaysDate = new Date();
+        final Date closureDate = closureCommand.getClosingDate().toDate();
+        if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(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(
+                    GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
+        }
+        final LocalDate incomeAndExpenseBookOffDate = LocalDate.fromDateFields(closureDate);
+
+         /*get all offices underneath it valid closure date for all, get Jl for all and make bookings*/
+       // final List<Office> childOffices = office.getChildren();
+        final Collection<Long> childOfficesByHierarchy = this.officeReadPlatformService.officeByHierarchy(officeId);
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final GLClosure latestChildGlClosure = this.glClosureRepository.getLatestGLClosureByBranch(childOffice);
+                if (latestChildGlClosure != null) {
+                    if (latestChildGlClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
+                            GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestChildGlClosure.getClosingDate()); }
+                }
+            }
+        }
+
+        Collection<IncomeAndExpenseBookingData> incomeAndExpenseBookingCollection = new ArrayList<>();
+        final Long equityGlAccountId = closureCommand.getEquityGlAccountId();
+
+        final GLAccount glAccount= this.glAccountRepository.findOneWithNotFoundDetection(equityGlAccountId);
+
+        if(glAccount == null){throw new GLAccountNotFoundException(equityGlAccountId);}
+
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.
+                        retrieveAllIncomeAndExpenseJournalEntryData(childOffice, incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+                final IncomeAndExpenseBookingData incomeAndExpBookingData = this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true, glAccount,this.officeRepository.findOneWithNotFoundDetection(childOffice));
+                if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+            }
+        }else{
+            final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.retrieveAllIncomeAndExpenseJournalEntryData(officeId,incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+            final IncomeAndExpenseBookingData incomeAndExpBookingData= this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true,glAccount,office);
+            if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+        }
+        return incomeAndExpenseBookingCollection;
+    }
+
+    public IncomeAndExpenseBookingData bookOffIncomeAndExpense(final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList,
+                                                                final GLClosureCommand closureData,final boolean preview,final GLAccount glAccount,final Office office){
+        /* All running balances has to be calculated before booking off income and expense account */
+        boolean isRunningBalanceCalculated = true;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJournalEntryDataList ){
+            if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+        }
+        BigDecimal debits = BigDecimal.ZERO;
+        BigDecimal credits = BigDecimal.ZERO;
+
+        final List<SingleDebitOrCreditEntryData>  debitsJournalEntry = new ArrayList<>();
+        final List<SingleDebitOrCreditEntryData>  creditsJournalEntry = new ArrayList<>();
+
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    debits = debits.add(incomeAndExpense.getOfficeRunningBalance());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance(),null));
+                }else{
+                    credits= credits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    credits = credits.add(incomeAndExpense.getOfficeRunningBalance());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }else{
+                    debits= debits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));
+                }
+            }
+        }
+        final LocalDate today = DateUtils.getLocalDateOfTenant();
+        final int compare = debits.compareTo(credits);
+        BigDecimal difference = BigDecimal.ZERO;
+        JournalEntryData journalEntry = null;
+        if(compare == 1){
+            /* book with target gl id on the credit side */
+            difference = debits.subtract(credits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            creditsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }else if(compare == -1){
+            /* book with target gl id on the debit side*/
+            difference = credits.subtract(debits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            debitsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }
+        else if(compare == 0){
+            //throw new RunningBalanceZeroException(office.getName());

Review comment:
       https://github.com/muellners/fineract/blob/38e19f73fcea5c9165d34d9c83a7be8bf6cec884/fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/GLClosureWritePlatformServiceJpaRepositoryImpl.java#L353
   
   Kinda of strange , in my repo I updated this, but does it not reflect here,




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r418995870



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/GLClosureWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -148,4 +261,99 @@ private void handleGLClosureIntegrityIssues(final JsonCommand command, final Dat
         throw new PlatformDataIntegrityException("error.msg.glClosure.unknown.data.integrity.issue",
                 "Unknown data integrity issue with resource GL Closure: " + realCause.getMessage());
     }
+    private void handleRunningBalanceNotCalculated(final HashMap<Long,List<IncomeAndExpenseJournalEntryData>> allIncomeAndExpenseJLWithSubBranches){
+        final Iterator<Map.Entry<Long,List<IncomeAndExpenseJournalEntryData>>> iterator = allIncomeAndExpenseJLWithSubBranches.entrySet().iterator();
+        while(iterator.hasNext()){
+            final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJL = iterator.next().getValue();
+            for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJL ){
+                if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+            }
+        }
+    }
+    private String bookOffIncomeAndExpense(final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList,
+            final GLClosureCommand closureData,final GLAccount glAccount, final Office office){
+        /* All running balances has to be calculated before booking off income and expense account */
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJournalEntryDataList ){
+            if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+        }
+        BigDecimal debits = BigDecimal.ZERO;
+        BigDecimal credits = BigDecimal.ZERO;
+
+        int i = 0;
+        int j = 0;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){debits = debits.add(incomeAndExpense.getOfficeRunningBalance());
+                i++;
+                }
+                else{ credits = credits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                j++;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){credits = credits.add(incomeAndExpense.getOfficeRunningBalance());
+                j++;
+                }
+                else{debits= debits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                i++;
+                }
+            }
+        }
+        final int compare = debits.compareTo(credits);
+        BigDecimal difference = BigDecimal.ZERO;
+        JournalEntryCommand journalEntryCommand = null;
+        if(compare ==1){ j++;}else{
+            i++;
+        }
+
+        SingleDebitOrCreditEntryCommand[]  debitsJournalEntry  = new SingleDebitOrCreditEntryCommand[i];
+        SingleDebitOrCreditEntryCommand[]  creditsJournalEntry  = new SingleDebitOrCreditEntryCommand[j];
+        int m=0;
+        int n=0;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    debitsJournalEntry[m] = new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    m++;
+                }else{
+                    creditsJournalEntry[n]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    n++;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    creditsJournalEntry[n]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    n++;
+                }else{
+                    debitsJournalEntry[m]= new SingleDebitOrCreditEntryCommand(null,incomeAndExpense.getAccountId(),incomeAndExpense.getOfficeRunningBalance().abs(),null);
+                    m++;
+                }
+            }
+        }
+        String transactionId = null;
+        if(compare == 1){
+            /* book with target gl id on the credit side */
+            difference = debits.subtract(credits);
+            final SingleDebitOrCreditEntryCommand targetBooking = new SingleDebitOrCreditEntryCommand(null,closureData.getEquityGlAccountId(),difference,null);
+            creditsJournalEntry[n] = targetBooking;
+            journalEntryCommand = new JournalEntryCommand(office.getId(),closureData.getCurrencyCode(),closureData.getClosingDate(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,closureData.getIncomeAndExpenseComments(),
+                    null,null,null,null,null,null,null,null);
+            transactionId = this.journalEntryWritePlatformService.createJournalEntryForIncomeAndExpenseBookOff(journalEntryCommand);
+
+        }else if(compare == -1){
+            /* book with target gl id on the debit side*/
+            difference = credits.subtract(debits);
+            final SingleDebitOrCreditEntryCommand targetBooking = new SingleDebitOrCreditEntryCommand(null,closureData.getEquityGlAccountId(),difference,null);
+            debitsJournalEntry[m]= targetBooking;
+            journalEntryCommand = new JournalEntryCommand(office.getId(),closureData.getCurrencyCode(),closureData.getClosingDate(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,closureData.getIncomeAndExpenseComments(),
+                    null,null,null,null,null,null,null,null);
+            transactionId = this.journalEntryWritePlatformService.createJournalEntryForIncomeAndExpenseBookOff(journalEntryCommand);
+        }else if(compare == 0){
+            //throw new RunningBalanceZeroException(office.getName());
+            return null;

Review comment:
       I did change it to throw.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-632837398


   Yeah , I will put some time on the pending integration test. Give me couple of days,


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-636497085


   @maektwain friendly ping / reminder - are you still planning to work on finishing this one? Rebase it.


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-632829603


   @maektwain friendly ping / reminder - are you still planning to work on finishing this one?


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] vorburger commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
vorburger commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r420001038



##########
File path: fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/CalculateIncomeAndExpenseBookingIntegrationTest.java
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.integrationtests;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.service.CalculateIncomeAndExpenseBookingImpl;
+import org.apache.fineract.accounting.closure.service.IncomeAndExpenseReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+public class CalculateIncomeAndExpenseBookingIntegrationTest {

Review comment:
       Isn't this actually a Unit and not an Integration Test? It doesn't seem to make any REST calls (as far as I can tell). If that is correct, then how about moving this to `src/test` instead of `src/integregationTest`?

##########
File path: fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/CalculateIncomeAndExpenseBookingIntegrationTest.java
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.integrationtests;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.service.CalculateIncomeAndExpenseBookingImpl;
+import org.apache.fineract.accounting.closure.service.IncomeAndExpenseReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+public class CalculateIncomeAndExpenseBookingIntegrationTest {
+
+    private JsonCommand jsonCommandWrapperTest;
+    private IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private OfficeReadPlatformService officeReadPlatformService;
+    private CalculateIncomeAndExpenseBookingImpl calculateIncomeAndExpenseBooking;
+
+    @Before
+    public void setup() {
+        calculateIncomeAndExpenseBooking =  new CalculateIncomeAndExpenseBookingImpl(null, null, null, null, incomeAndExpenseReadPlatformService,officeReadPlatformService);
+    }
+
+    public JsonCommand getCommand() {
+        return jsonCommandWrapperTest;
+    }
+
+    public JsonCommand setCommand(JsonCommand command) {
+        return this.jsonCommandWrapperTest = command;
+    }
+
+    /*
+    Case 1: All running balances has to be calculated before booking off income and expense account
+    If not running balances, then throw exception
+     */
+    @Test(expected = RunningBalanceNotCalculatedException.class)
+    public void testBookOffIncomeAndExpenseCheckException() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand((long)10,(long)10, new LocalDate(),"Closing comment",  false, (long)10 , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, false, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList,glClosureCommand, false,null,null);
+    }
+
+    /*
+    Case 2: All running balances has to be calculated before booking off income and expense account
+    No exception is to be thrown since it is running for the input.
+    However, later a null pointer exception is thrown, since the object incomeAndExpense is not initialized.
+     */
+    @Test
+    public void testBookOffIncomeAndExpense() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, true, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+    Case 3: In the case of an income account type-  if the OfficeRunningBalance is greater than 0, then add to debits
+     */
+    @Test(expected = NoClassDefFoundError.class)

Review comment:
       This (`@Test(expected = NoClassDefFoundError.class)`) seems really weird to me, at first review. Not the expecting an exception, that is fine, as above in `testBookOffIncomeAndExpenseCheckException` for `RunningBalanceNotCalculatedException`, but why would a "functional" test ever hit a `NoClassDefFoundError`, which is a (very) low-level technical exception that (normally) "should never happen"?

##########
File path: fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/CalculateIncomeAndExpenseBookingIntegrationTest.java
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.integrationtests;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.service.CalculateIncomeAndExpenseBookingImpl;
+import org.apache.fineract.accounting.closure.service.IncomeAndExpenseReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+public class CalculateIncomeAndExpenseBookingIntegrationTest {
+
+    private JsonCommand jsonCommandWrapperTest;
+    private IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private OfficeReadPlatformService officeReadPlatformService;
+    private CalculateIncomeAndExpenseBookingImpl calculateIncomeAndExpenseBooking;
+
+    @Before
+    public void setup() {
+        calculateIncomeAndExpenseBooking =  new CalculateIncomeAndExpenseBookingImpl(null, null, null, null, incomeAndExpenseReadPlatformService,officeReadPlatformService);
+    }
+
+    public JsonCommand getCommand() {
+        return jsonCommandWrapperTest;
+    }
+
+    public JsonCommand setCommand(JsonCommand command) {
+        return this.jsonCommandWrapperTest = command;
+    }
+
+    /*
+    Case 1: All running balances has to be calculated before booking off income and expense account
+    If not running balances, then throw exception
+     */
+    @Test(expected = RunningBalanceNotCalculatedException.class)
+    public void testBookOffIncomeAndExpenseCheckException() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand((long)10,(long)10, new LocalDate(),"Closing comment",  false, (long)10 , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, false, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList,glClosureCommand, false,null,null);
+    }
+
+    /*
+    Case 2: All running balances has to be calculated before booking off income and expense account
+    No exception is to be thrown since it is running for the input.
+    However, later a null pointer exception is thrown, since the object incomeAndExpense is not initialized.
+     */
+    @Test
+    public void testBookOffIncomeAndExpense() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, true, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+    Case 3: In the case of an income account type-  if the OfficeRunningBalance is greater than 0, then add to debits
+     */
+    @Test(expected = NoClassDefFoundError.class)
+    public void testIncomeAccountsRunningBalanceGreaterThanZero_Debit() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(10),null,4,10,null,null);
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData2= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(20),null,4,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData2);
+        IncomeAndExpenseBookingData incomeAndExpenseBookingData = calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false, null, null);
+        incomeAndExpenseBookingData.getJournalEntries().forEach(entry->{
+            Assert.assertEquals(entry.getDebits().get(0).getAmount(), new BigDecimal(30));
+        });
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+    Case 4: In the case of an income account type-  if the OfficeRunningBalance is less than 0, then add to credits
+     */
+    @Test(expected = NoClassDefFoundError.class)
+    public void testIncomeAccountsRunningBalanceLessThanZero_Credit() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(-10),null,4,10,null,null);
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData2= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(-20),null,4,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData2);
+        IncomeAndExpenseBookingData incomeAndExpenseBookingData = calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false, null, null);
+        incomeAndExpenseBookingData.getJournalEntries().forEach(entry->{
+            Assert.assertEquals(entry.getCredits().get(0).getAmount(), new BigDecimal(30));
+        });
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+Case 5: In the case of an Expanse account type-  if the OfficeRunningBalance is greater than 0, then add to credit
+     */
+    @Test(expected = NoClassDefFoundError.class)
+    public void testIncomeAccountsRunningBalanceGreaterThanZero_Credit() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(10),null,5,10,null,null);
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData2= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(20),null,5,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData2);
+        IncomeAndExpenseBookingData incomeAndExpenseBookingData = calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false, null, null);
+        incomeAndExpenseBookingData.getJournalEntries().forEach(entry->{
+            Assert.assertEquals(entry.getDebits().get(0).getAmount(), new BigDecimal(30));
+        });
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+Case 6: In the case of an Expanse account type- if the OfficeRunningBalance is less than 0, then add to debits
+     */
+    @Test(expected = NoClassDefFoundError.class)

Review comment:
       again

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/domain/IncomeAndExpenseBooking.java
##########
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.accounting.closure.domain;
+
+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.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.organisation.office.domain.Office;
+
+
+@Entity
+@Table(name = "acc_income_and_expense_bookings",uniqueConstraints = { @UniqueConstraint(columnNames = { "journal_entry_transaction_id" }, name = "journal_entry_transaction_id") })
+public class IncomeAndExpenseBooking extends AbstractPersistableCustom  {
+
+    @ManyToOne
+    @JoinColumn(name = "gl_closure_id", nullable = false)
+    private GLClosure glClosure;
+    @Column(name = "journal_entry_transaction_id",nullable = false)
+    private String transactionId;
+    @ManyToOne
+    @JoinColumn(name = "office_id", nullable = false)
+    private Office office;
+    @Column(name = "is_reversed", nullable = false)
+    private boolean reversed;

Review comment:
       Minor formatting nit pick: How about a blank line space between each annotated JPA field?

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -625,6 +622,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       but it's still done the old one, didn't you mean to change this, after the rebase?

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/service/CalculateIncomeAndExpenseBookingImpl.java
##########
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.accounting.closure.service;
+
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.data.JournalEntryData;
+import org.apache.fineract.accounting.closure.data.SingleDebitOrCreditEntryData;
+import org.apache.fineract.accounting.closure.domain.GLClosure;
+import org.apache.fineract.accounting.closure.domain.GLClosureRepository;
+import org.apache.fineract.accounting.closure.exception.GLClosureInvalidException;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.serialization.GLClosureCommandFromApiJsonDeserializer;
+import org.apache.fineract.accounting.glaccount.domain.GLAccount;
+import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
+import org.apache.fineract.accounting.glaccount.exception.GLAccountNotFoundException;
+import org.apache.fineract.infrastructure.core.api.JsonQuery;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.organisation.office.domain.Office;
+import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
+import org.apache.fineract.organisation.office.exception.OfficeNotFoundException;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+@Service
+public class CalculateIncomeAndExpenseBookingImpl implements CalculateIncomeAndExpenseBooking {
+
+    private final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer;
+    private final OfficeRepositoryWrapper officeRepository;
+    private final GLClosureRepository glClosureRepository;
+    private final GLAccountRepositoryWrapper glAccountRepository;
+    private final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private final OfficeReadPlatformService officeReadPlatformService;
+
+    @Autowired
+    public CalculateIncomeAndExpenseBookingImpl(final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer,
+            final OfficeRepositoryWrapper officeRepository,final GLClosureRepository glClosureRepository,
+            final GLAccountRepositoryWrapper glAccountRepository,final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService,
+            final OfficeReadPlatformService officeReadPlatformService) {
+        this.fromApiJsonDeserializer = fromApiJsonDeserializer;
+        this.officeRepository = officeRepository;
+        this.glClosureRepository = glClosureRepository;
+        this.glAccountRepository = glAccountRepository;
+        this.incomeAndExpenseReadPlatformService = incomeAndExpenseReadPlatformService;
+        this.officeReadPlatformService = officeReadPlatformService;
+    }
+
+    @Override
+    public Collection<IncomeAndExpenseBookingData> CalculateIncomeAndExpenseBookings(JsonQuery query) {
+        final GLClosureCommand closureCommand = this.fromApiJsonDeserializer.commandFromApiJson(query.json());
+        closureCommand.validateForCreate();
+        final Long officeId = closureCommand.getOfficeId();
+        final Office office = this.officeRepository.findOneWithNotFoundDetection(officeId);
+        if (office == null) { throw new OfficeNotFoundException(officeId); }
+
+        final Date todaysDate = new Date();
+        final Date closureDate = closureCommand.getClosingDate().toDate();
+        if (closureDate.after(todaysDate)) { throw new GLClosureInvalidException(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(
+                    GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); }
+        }
+        final LocalDate incomeAndExpenseBookOffDate = LocalDate.fromDateFields(closureDate);
+
+         /*get all offices underneath it valid closure date for all, get Jl for all and make bookings*/
+       // final List<Office> childOffices = office.getChildren();
+        final Collection<Long> childOfficesByHierarchy = this.officeReadPlatformService.officeByHierarchy(officeId);
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final GLClosure latestChildGlClosure = this.glClosureRepository.getLatestGLClosureByBranch(childOffice);
+                if (latestChildGlClosure != null) {
+                    if (latestChildGlClosure.getClosingDate().after(closureDate)) { throw new GLClosureInvalidException(
+                            GLClosureInvalidException.GL_CLOSURE_INVALID_REASON.ACCOUNTING_CLOSED, latestChildGlClosure.getClosingDate()); }
+                }
+            }
+        }
+
+        Collection<IncomeAndExpenseBookingData> incomeAndExpenseBookingCollection = new ArrayList<>();
+        final Long equityGlAccountId = closureCommand.getEquityGlAccountId();
+
+        final GLAccount glAccount= this.glAccountRepository.findOneWithNotFoundDetection(equityGlAccountId);
+
+        if(glAccount == null){throw new GLAccountNotFoundException(equityGlAccountId);}
+
+        if(closureCommand.getSubBranches() && childOfficesByHierarchy.size() > 0){
+            for(final Long childOffice : childOfficesByHierarchy){
+                final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.
+                        retrieveAllIncomeAndExpenseJournalEntryData(childOffice, incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+                final IncomeAndExpenseBookingData incomeAndExpBookingData = this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true, glAccount,this.officeRepository.findOneWithNotFoundDetection(childOffice));
+                if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+            }
+        }else{
+            final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = this.incomeAndExpenseReadPlatformService.retrieveAllIncomeAndExpenseJournalEntryData(officeId,incomeAndExpenseBookOffDate,closureCommand.getCurrencyCode());
+            final IncomeAndExpenseBookingData incomeAndExpBookingData= this.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, closureCommand, true,glAccount,office);
+            if(incomeAndExpBookingData.getJournalEntries().size() > 0){ incomeAndExpenseBookingCollection.add(incomeAndExpBookingData);}
+        }
+        return incomeAndExpenseBookingCollection;
+    }
+
+    public IncomeAndExpenseBookingData bookOffIncomeAndExpense(final List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList,
+                                                                final GLClosureCommand closureData,final boolean preview,final GLAccount glAccount,final Office office){
+        /* All running balances has to be calculated before booking off income and expense account */
+        boolean isRunningBalanceCalculated = true;
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpenseData :incomeAndExpenseJournalEntryDataList ){
+            if(!incomeAndExpenseData.isRunningBalanceCalculated()){ throw new RunningBalanceNotCalculatedException(incomeAndExpenseData.getOfficeId());}
+        }
+        BigDecimal debits = BigDecimal.ZERO;
+        BigDecimal credits = BigDecimal.ZERO;
+
+        final List<SingleDebitOrCreditEntryData>  debitsJournalEntry = new ArrayList<>();
+        final List<SingleDebitOrCreditEntryData>  creditsJournalEntry = new ArrayList<>();
+
+        for(final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList){
+            if(incomeAndExpense.isIncomeAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    debits = debits.add(incomeAndExpense.getOfficeRunningBalance());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance(),null));
+                }else{
+                    credits= credits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }
+            }
+            if(incomeAndExpense.isExpenseAccountType()){
+                if(incomeAndExpense.getOfficeRunningBalance().signum() == 1){
+                    credits = credits.add(incomeAndExpense.getOfficeRunningBalance());
+                    creditsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));;
+                }else{
+                    debits= debits.add(incomeAndExpense.getOfficeRunningBalance().abs());
+                    debitsJournalEntry.add(new SingleDebitOrCreditEntryData(incomeAndExpense.getAccountId(),incomeAndExpense.getGlAccountName(),incomeAndExpense.getOfficeRunningBalance().abs(),null));
+                }
+            }
+        }
+        final LocalDate today = DateUtils.getLocalDateOfTenant();
+        final int compare = debits.compareTo(credits);
+        BigDecimal difference = BigDecimal.ZERO;
+        JournalEntryData journalEntry = null;
+        if(compare == 1){
+            /* book with target gl id on the credit side */
+            difference = debits.subtract(credits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            creditsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }else if(compare == -1){
+            /* book with target gl id on the debit side*/
+            difference = credits.subtract(debits);
+            SingleDebitOrCreditEntryData targetBooking = new SingleDebitOrCreditEntryData(closureData.getEquityGlAccountId(),glAccount.getName(),difference,null);
+            debitsJournalEntry.add(targetBooking);
+            journalEntry = new JournalEntryData(office.getId(),today.toString(),closureData.getComments(),creditsJournalEntry,debitsJournalEntry,null,false,closureData.getCurrencyCode(),office.getName());
+        }
+        else if(compare == 0){
+            //throw new RunningBalanceZeroException(office.getName());

Review comment:
       ?

##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java
##########
@@ -626,6 +623,63 @@ private void saveAllDebitOrCreditEntries(final JournalEntryCommand command, fina
         }
     }
 
+    @Transactional
+    @Override
+    public String createJournalEntryForIncomeAndExpenseBookOff(final JournalEntryCommand journalEntryCommand) {
+        try{
+            journalEntryCommand.validateForCreate();
+
+            // check office is valid
+            final Long officeId = journalEntryCommand.getOfficeId();
+            final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
+
+            final String currencyCode = journalEntryCommand.getCurrencyCode();
+
+            validateBusinessRulesForJournalEntries(journalEntryCommand);
+            /** Capture payment details **/
+            final PaymentDetail paymentDetail =null;
+
+            /** Set a transaction Id and save these Journal entries **/
+            final Date transactionDate = journalEntryCommand.getTransactionDate().toDate();
+            final String transactionId = generateTransactionId(officeId);
+            final String referenceNumber = journalEntryCommand.getReferenceNumber();
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getDebits(), transactionId, JournalEntryType.DEBIT, referenceNumber);
+
+            debitOrCreditEntriesForIncomeAndExpenseBooking(journalEntryCommand, office, paymentDetail, currencyCode, transactionDate,
+                    journalEntryCommand.getCredits(), transactionId, JournalEntryType.CREDIT, referenceNumber);
+
+            return transactionId;
+        }catch (final DataIntegrityViolationException dve) {
+            handleJournalEntryDataIntegrityIssues(dve);
+            return null;

Review comment:
       ```suggestion
               throw handleJournalEntryDataIntegrityIssues(dve);
   ```

##########
File path: fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/CalculateIncomeAndExpenseBookingIntegrationTest.java
##########
@@ -0,0 +1,161 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fineract.integrationtests;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.fineract.accounting.closure.command.GLClosureCommand;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseBookingData;
+import org.apache.fineract.accounting.closure.data.IncomeAndExpenseJournalEntryData;
+import org.apache.fineract.accounting.closure.exception.RunningBalanceNotCalculatedException;
+import org.apache.fineract.accounting.closure.service.CalculateIncomeAndExpenseBookingImpl;
+import org.apache.fineract.accounting.closure.service.IncomeAndExpenseReadPlatformService;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
+import org.joda.time.LocalDate;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+
+public class CalculateIncomeAndExpenseBookingIntegrationTest {
+
+    private JsonCommand jsonCommandWrapperTest;
+    private IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService;
+    private OfficeReadPlatformService officeReadPlatformService;
+    private CalculateIncomeAndExpenseBookingImpl calculateIncomeAndExpenseBooking;
+
+    @Before
+    public void setup() {
+        calculateIncomeAndExpenseBooking =  new CalculateIncomeAndExpenseBookingImpl(null, null, null, null, incomeAndExpenseReadPlatformService,officeReadPlatformService);
+    }
+
+    public JsonCommand getCommand() {
+        return jsonCommandWrapperTest;
+    }
+
+    public JsonCommand setCommand(JsonCommand command) {
+        return this.jsonCommandWrapperTest = command;
+    }
+
+    /*
+    Case 1: All running balances has to be calculated before booking off income and expense account
+    If not running balances, then throw exception
+     */
+    @Test(expected = RunningBalanceNotCalculatedException.class)
+    public void testBookOffIncomeAndExpenseCheckException() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand((long)10,(long)10, new LocalDate(),"Closing comment",  false, (long)10 , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, false, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList,glClosureCommand, false,null,null);
+    }
+
+    /*
+    Case 2: All running balances has to be calculated before booking off income and expense account
+    No exception is to be thrown since it is running for the input.
+    However, later a null pointer exception is thrown, since the object incomeAndExpense is not initialized.
+     */
+    @Test
+    public void testBookOffIncomeAndExpense() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null
+                , true, true, null,null,null,10,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+    Case 3: In the case of an income account type-  if the OfficeRunningBalance is greater than 0, then add to debits
+     */
+    @Test(expected = NoClassDefFoundError.class)
+    public void testIncomeAccountsRunningBalanceGreaterThanZero_Debit() {
+        GLClosureCommand glClosureCommand =  new GLClosureCommand(10L, 10L, new LocalDate(), "Closing comment", false, 10L , "CAD", false, false, "comment" );
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(10),null,4,10,null,null);
+        IncomeAndExpenseJournalEntryData incomeAndExpenseJournalEntryData2= new IncomeAndExpenseJournalEntryData(null, null,null, null, true, true, null,new BigDecimal(20),null,4,10,null,null);
+        List<IncomeAndExpenseJournalEntryData> incomeAndExpenseJournalEntryDataList = new ArrayList<>();
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData);
+        incomeAndExpenseJournalEntryDataList.add(incomeAndExpenseJournalEntryData2);
+        IncomeAndExpenseBookingData incomeAndExpenseBookingData = calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false, null, null);
+        incomeAndExpenseBookingData.getJournalEntries().forEach(entry->{
+            Assert.assertEquals(entry.getDebits().get(0).getAmount(), new BigDecimal(30));
+        });
+        Assert.assertNotNull(calculateIncomeAndExpenseBooking.bookOffIncomeAndExpense(incomeAndExpenseJournalEntryDataList, glClosureCommand, false,null,null));
+    }
+
+    /*
+    Case 4: In the case of an income account type-  if the OfficeRunningBalance is less than 0, then add to credits
+     */
+    @Test(expected = NoClassDefFoundError.class)

Review comment:
       as above




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on a change in pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on a change in pull request #796:
URL: https://github.com/apache/fineract/pull/796#discussion_r418996418



##########
File path: fineract-provider/src/main/java/org/apache/fineract/accounting/closure/api/GLClosuresApiResource.java
##########
@@ -143,6 +159,21 @@ public String createGLClosure(@ApiParam(hidden = true) final String jsonRequestB
         return this.apiJsonSerializerService.serialize(result);
     }
 
+    @POST
+    @Path("previewIncomeAndExpense")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    @ApiOperation(value = "Create an End of the Year Accounting Closure")
+    public String previewIncomeAndExpenseBooking(@Context final UriInfo uriInfo, final String apiRequestBodyAsJson) {
+            final JsonElement parsedQuery = this.fromJsonHelper.parse(apiRequestBodyAsJson);
+            final JsonQuery query = JsonQuery.from(apiRequestBodyAsJson, parsedQuery, this.fromJsonHelper);
+            final Collection<IncomeAndExpenseBookingData> incomeAndExpenseBookingCollection = this.calculateIncomeAndExpenseBooking.CalculateIncomeAndExpenseBookings(query);
+            final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
+            return this.incomeAndExpenseBookingDataDefaultToApiJsonSerializer.serialize(settings, incomeAndExpenseBookingCollection, new HashSet<String>());

Review comment:
       Ack.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] maektwain commented on pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
maektwain commented on pull request #796:
URL: https://github.com/apache/fineract/pull/796#issuecomment-622983431


   @vorburger Let's see, if this fails, at my end it passed. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [fineract] github-actions[bot] closed pull request #796: FINERACT-38

Posted by GitBox <gi...@apache.org>.
github-actions[bot] closed pull request #796:
URL: https://github.com/apache/fineract/pull/796


   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org