Java tutorial
/** * 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.bookoffincomeandexpense.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.bookoffincomeandexpense.data.IncomeAndExpenseBookingData; import org.apache.fineract.accounting.closure.bookoffincomeandexpense.data.IncomeAndExpenseJournalEntryData; import org.apache.fineract.accounting.closure.bookoffincomeandexpense.data.JournalEntry; import org.apache.fineract.accounting.closure.bookoffincomeandexpense.data.SingleDebitOrCreditEntry; import org.apache.fineract.accounting.closure.bookoffincomeandexpense.exception.RunningBalanceNotCalculatedException; import org.apache.fineract.accounting.closure.command.GLClosureCommand; 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.serialization.GLClosureCommandFromApiJsonDeserializer; import org.apache.fineract.accounting.glaccount.domain.GLAccount; import org.apache.fineract.accounting.glaccount.domain.GLAccountRepository; 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.OfficeRepository; 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 OfficeRepository officeRepository; private final GLClosureRepository glClosureRepository; private final GLAccountRepository glAccountRepository; private final IncomeAndExpenseReadPlatformService incomeAndExpenseReadPlatformService; private final OfficeReadPlatformService officeReadPlatformService; @Autowired public CalculateIncomeAndExpenseBookingImpl( final GLClosureCommandFromApiJsonDeserializer fromApiJsonDeserializer, final OfficeRepository officeRepository, final GLClosureRepository glClosureRepository, final GLAccountRepository 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.findOne(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.findOne(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.findOne(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; } private 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<SingleDebitOrCreditEntry> debitsJournalEntry = new ArrayList<>(); final List<SingleDebitOrCreditEntry> creditsJournalEntry = new ArrayList<>(); for (final IncomeAndExpenseJournalEntryData incomeAndExpense : incomeAndExpenseJournalEntryDataList) { if (incomeAndExpense.isIncomeAccountType()) { if (incomeAndExpense.getOfficeRunningBalance().signum() == 1) { debits = debits.add(incomeAndExpense.getOfficeRunningBalance()); debitsJournalEntry.add(new SingleDebitOrCreditEntry(incomeAndExpense.getAccountId(), incomeAndExpense.getGlAccountName(), incomeAndExpense.getOfficeRunningBalance(), null)); } else { credits = credits.add(incomeAndExpense.getOfficeRunningBalance().abs()); creditsJournalEntry.add(new SingleDebitOrCreditEntry(incomeAndExpense.getAccountId(), incomeAndExpense.getGlAccountName(), incomeAndExpense.getOfficeRunningBalance().abs(), null)); ; } } if (incomeAndExpense.isExpenseAccountType()) { if (incomeAndExpense.getOfficeRunningBalance().signum() == 1) { credits = credits.add(incomeAndExpense.getOfficeRunningBalance()); creditsJournalEntry.add(new SingleDebitOrCreditEntry(incomeAndExpense.getAccountId(), incomeAndExpense.getGlAccountName(), incomeAndExpense.getOfficeRunningBalance().abs(), null)); ; } else { debits = debits.add(incomeAndExpense.getOfficeRunningBalance().abs()); debitsJournalEntry.add(new SingleDebitOrCreditEntry(incomeAndExpense.getAccountId(), incomeAndExpense.getGlAccountName(), incomeAndExpense.getOfficeRunningBalance().abs(), null)); } } } final LocalDate today = DateUtils.getLocalDateOfTenant(); final int compare = debits.compareTo(credits); BigDecimal difference = BigDecimal.ZERO; JournalEntry journalEntry = null; if (compare == 1) { /* book with target gl id on the credit side */ difference = debits.subtract(credits); SingleDebitOrCreditEntry targetBooking = new SingleDebitOrCreditEntry( closureData.getEquityGlAccountId(), glAccount.getName(), difference, null); creditsJournalEntry.add(targetBooking); journalEntry = new JournalEntry(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); SingleDebitOrCreditEntry targetBooking = new SingleDebitOrCreditEntry( closureData.getEquityGlAccountId(), glAccount.getName(), difference, null); debitsJournalEntry.add(targetBooking); journalEntry = new JournalEntry(office.getId(), today.toString(), closureData.getComments(), creditsJournalEntry, debitsJournalEntry, null, false, closureData.getCurrencyCode(), office.getName()); } else if (compare == 0) { //throw new RunningBalanceZeroException(office.getName()); } final LocalDate localDate = LocalDate.now(); final List<JournalEntry> journalEntries = new ArrayList<>(); if (journalEntry != null) { journalEntries.add(journalEntry); } return new IncomeAndExpenseBookingData(localDate, closureData.getComments(), journalEntries); } }