KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opensourcestrategies > financials > financials > FinancialServices


1 /*
2  * Copyright (c) 2006 - 2007 Open Source Strategies, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the Honest Public License.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * Honest Public License for more details.
11  *
12  * You should have received a copy of the Honest Public License
13  * along with this program; if not, write to Funambol,
14  * 643 Bair Island Road, Suite 305 - Redwood City, CA 94063, USA
15  */

16
17 package com.opensourcestrategies.financials.financials;
18
19 import java.util.Date JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.sql.Timestamp JavaDoc;
26 import java.math.BigDecimal JavaDoc;
27
28 import javolution.util.FastMap;
29
30 import org.ofbiz.accounting.util.UtilAccounting;
31 import org.ofbiz.base.util.Debug;
32 import org.ofbiz.base.util.UtilDateTime;
33 import org.ofbiz.base.util.UtilMisc;
34 import org.ofbiz.base.util.UtilNumber;
35 import org.ofbiz.base.util.UtilValidate;
36 import org.ofbiz.entity.GenericDelegator;
37 import org.ofbiz.entity.GenericEntityException;
38 import org.ofbiz.entity.GenericValue;
39 import org.ofbiz.entity.condition.EntityConditionList;
40 import org.ofbiz.entity.condition.EntityExpr;
41 import org.ofbiz.entity.condition.EntityOperator;
42 import org.ofbiz.entity.util.EntityUtil;
43 import org.ofbiz.service.DispatchContext;
44 import org.ofbiz.service.GenericServiceException;
45 import org.ofbiz.service.LocalDispatcher;
46 import org.ofbiz.service.ModelService;
47 import org.ofbiz.service.ServiceUtil;
48
49 import com.opensourcestrategies.financials.util.UtilFinancial;
50
51 /**
52  * FinancialServices - Services for generating financial reports and statements
53  *
54  * @author <a HREF="mailto:sichen@opensourcestrategies.com">Si Chen</a>
55  * @version $Rev: 553 $
56  * @since 2.2
57  */

58
59 public class FinancialServices {
60     
61     public static String JavaDoc module = FinancialServices.class.getName();
62
63     public static int decimals = UtilNumber.getBigDecimalScale("fin_arithmetic.properties", "financial.statements.decimals");
64     public static int rounding = UtilNumber.getBigDecimalRoundingMode("fin_arithmetic.properties", "financial.statements.rounding");
65     public static final BigDecimal JavaDoc ZERO = (new BigDecimal JavaDoc("0")).setScale(decimals, rounding);
66     
67     /**
68      * Generates an income statement over two CustomTimePeriod entries, returning a Map of GlAccount and amounts and a double
69      */

70     public static Map JavaDoc getIncomeStatementByTimePeriods(DispatchContext dctx, Map JavaDoc context) {
71         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
72         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
73         GenericValue userLogin = (GenericValue) context.get("userLogin");
74         Map JavaDoc input = UtilMisc.toMap("organizationPartyId", organizationPartyId, "glFiscalTypeId", glFiscalTypeId, "userLogin", userLogin);
75         return reportServiceTimePeriodHelper(dctx, context, "getIncomeStatementByDates", input);
76     }
77     
78     /**
79      * generates an income statement over a range of dates, returning a Map of GlAccount and amounts and a netIncome
80      */

81     public static Map JavaDoc getIncomeStatementByDates(DispatchContext dctx, Map JavaDoc context) {
82         LocalDispatcher dispatcher = dctx.getDispatcher();
83         GenericDelegator delegator = dctx.getDelegator();
84         Timestamp JavaDoc fromDate = (Timestamp JavaDoc) context.get("fromDate");
85         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
86         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
87         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
88         GenericValue userLogin = (GenericValue) context.get("userLogin");
89         // glFiscalTypeId defaults to ACTUAL
90
if ((glFiscalTypeId == null) || (glFiscalTypeId.equals(""))) {
91             glFiscalTypeId = "ACTUAL";
92         }
93         
94         try {
95             // get a Map of glAccount -> sums for all income statement accounts for this time period
96
Map JavaDoc tmpResult = dispatcher.runSync("getIncomeStatementAccountSumsByDate", UtilMisc.toMap("organizationPartyId", organizationPartyId, "fromDate", fromDate,
97                     "thruDate", thruDate, "glFiscalTypeId", glFiscalTypeId, "userLogin", userLogin));
98             if (tmpResult.get("glAccountSums") == null) {
99                 return ServiceUtil.returnError("Cannot sum up account balances properly for income statement");
100             }
101             Map JavaDoc glAccountSums = (HashMap JavaDoc) tmpResult.get("glAccountSums");
102             
103             // final phase - figure out net income by adding and subtracting the sum for each account, depending on whether it is an INCOME or
104
// EXPENSE account. This is the part where we also flip the signs of the sums, so the EXPENSE account amounts show up as NEGATIVE
105
glAccountSums = (Map JavaDoc) tmpResult.get("glAccountSums");
106             double netIncome = 0.0;
107             List JavaDoc accounts = EntityUtil.orderBy(glAccountSums.keySet(), UtilMisc.toList("glAccountId"));
108             for (Iterator JavaDoc ai = accounts.iterator(); ai.hasNext(); ) {
109                GenericValue account = (GenericValue) ai.next();
110                double accountSum = ((Double JavaDoc) glAccountSums.get(account)).doubleValue();
111                if (UtilAccounting.isRevenueAccount(account) || UtilAccounting.isIncomeAccount(account)) {
112                    netIncome += accountSum;
113                } else if (UtilAccounting.isExpenseAccount(account)) {
114                    netIncome -= accountSum;
115                    glAccountSums.put(account, new Double JavaDoc(-accountSum));
116                }
117             }
118             
119             // was this income statement for periods which are closed? check by seeing if there are any unclosed time periods
120
// in between these dates? If so, then this accounting period has not been closed
121
// TODO: this is not very good. Implement a real service which checks through all interim periods correctly.
122
boolean isClosed = true;
123             EntityConditionList conditions = new EntityConditionList(UtilMisc.toList(
124                new EntityExpr("organizationPartyId", EntityOperator.EQUALS, organizationPartyId),
125                new EntityExpr("isClosed", EntityOperator.NOT_EQUAL, "Y"),
126                new EntityConditionList(UtilMisc.toList(
127                    new EntityExpr("fromDate", EntityOperator.GREATER_THAN_EQUAL_TO, fromDate),
128                    new EntityExpr("thruDate", EntityOperator.LESS_THAN_EQUAL_TO, thruDate)), EntityOperator.OR)), EntityOperator.AND);
129             List JavaDoc timePeriods = delegator.findByCondition("CustomTimePeriod", conditions, UtilMisc.toList("customTimePeriodId"), UtilMisc.toList("customTimePeriodId"));
130             if (timePeriods.size() > 0) {
131                 isClosed = false;
132             }
133
134             // now get the profit/loss GlAccount for the organization and return it as well
135
String JavaDoc retainedEarningsGlAccountId = null;
136             GenericValue retainedEarningsGlAccount = null;
137             
138             // get retained earnings account for the organization
139
tmpResult = dispatcher.runSync("getProductOrgGlAccount", UtilMisc.toMap("organizationPartyId", organizationPartyId,
140                     "glAccountTypeId", "RETAINED_EARNINGS", "userLogin", userLogin));
141             if (tmpResult.get("glAccountId") == null) {
142                 return ServiceUtil.returnError("Cannot find a PROFIT_LOSS_ACCOUNT for organization " + organizationPartyId);
143             } else {
144                 retainedEarningsGlAccountId = (String JavaDoc) tmpResult.get("glAccountId");
145                 retainedEarningsGlAccount = delegator.findByPrimaryKeyCache("GlAccount", UtilMisc.toMap("glAccountId", retainedEarningsGlAccountId));
146             }
147             
148             // all done
149
Map JavaDoc result = ServiceUtil.returnSuccess();
150             result.put("netIncome", new Double JavaDoc(netIncome));
151             result.put("glAccountSums", glAccountSums);
152             result.put("isClosed", new Boolean JavaDoc(isClosed));
153             result.put("retainedEarningsGlAccount", retainedEarningsGlAccount);
154             return result;
155         } catch (GenericEntityException ex) {
156             return(ServiceUtil.returnError(ex.getMessage()));
157         } catch (GenericServiceException ex) {
158             return(ServiceUtil.returnError(ex.getMessage()));
159         }
160     }
161     
162     /**
163      * Calculates net income (of ACTUAL gl fiscal type) since last closed accounting period or, if none exists, since earliest accounting period.
164      * Optionally use periodTypeId to get figure since last closed date of a period type */

165     public static Map JavaDoc getActualNetIncomeSinceLastClosing(DispatchContext dctx, Map JavaDoc context) {
166         LocalDispatcher dispatcher = dctx.getDispatcher();
167         GenericDelegator delegator = dctx.getDelegator();
168         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
169         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
170         GenericValue userLogin = (GenericValue) context.get("userLogin");
171         String JavaDoc periodTypeId = (String JavaDoc) context.get("periodTypeId");
172         
173         Timestamp JavaDoc fromDate = null;
174
175         try {
176             // try to get the ending date of the most recent accounting time period which has been closed
177
Map JavaDoc tmpResult = dispatcher.runSync("findLastClosedDate", UtilMisc.toMap("organizationPartyId", organizationPartyId,
178                     "periodTypeId", periodTypeId, "userLogin", userLogin));
179             if ((tmpResult != null) && (tmpResult.get("lastClosedDate") != null)) {
180                 fromDate = (Timestamp JavaDoc) tmpResult.get("lastClosedDate");
181             } else {
182                 return ServiceUtil.returnError("Cannot get a starting date for net income");
183             }
184
185             tmpResult = dispatcher.runSync("getIncomeStatementByDates", UtilMisc.toMap("organizationPartyId", organizationPartyId,
186                     "glFiscalTypeId", "ACTUAL", "fromDate", fromDate, "thruDate", thruDate, "userLogin", userLogin));
187             if (!(tmpResult.get(ModelService.RESPONSE_MESSAGE).equals(ModelService.RESPOND_SUCCESS))) {
188                 return tmpResult; // probably an error message - pass it back up
189
} else if (tmpResult.get("netIncome") == null) {
190                 return ServiceUtil.returnError("Cannot calculate a net income"); // no error message, no net income either?
191
} else {
192                 // return net income and profit&loss gl account
193
Map JavaDoc result = ServiceUtil.returnSuccess();
194                 result.put("netIncome", tmpResult.get("netIncome"));
195                 result.put("retainedEarningsGlAccount", tmpResult.get("retainedEarningsGlAccount"));
196                 return result;
197             }
198         } catch (GenericServiceException ex) {
199             return(ServiceUtil.returnError(ex.getMessage()));
200         }
201     }
202     
203     /**
204      * Generates balance sheet for a time period and returns separate maps for balances of asset, liability, and equity accounts */

205     public static Map JavaDoc getBalanceSheetForTimePeriod(DispatchContext dctx, Map JavaDoc context) {
206         LocalDispatcher dispatcher = dctx.getDispatcher();
207         GenericDelegator delegator = dctx.getDelegator();
208         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
209         String JavaDoc customTimePeriodId = (String JavaDoc) context.get("customTimePeriodId");
210         GenericValue userLogin = (GenericValue) context.get("userLogin");
211         
212         try {
213             // get the current time period and first use it to assume whether this period has been closed or not
214
GenericValue currentTimePeriod = delegator.findByPrimaryKey("CustomTimePeriod", UtilMisc.toMap("customTimePeriodId", customTimePeriodId));
215             boolean isClosed = false;
216             if (currentTimePeriod.getString("isClosed").equals("Y")) {
217                 isClosed = true;
218             }
219             // first, find all the gl accounts' GlAccountHistory record for this time period
220
EntityConditionList conditions = new EntityConditionList(UtilMisc.toList(
221                     new EntityExpr("organizationPartyId", EntityOperator.EQUALS, organizationPartyId),
222                     new EntityExpr("customTimePeriodId", EntityOperator.EQUALS, customTimePeriodId),
223                     new EntityConditionList(UtilMisc.toList(
224                         UtilFinancial.getAssetExpr(delegator),
225                         UtilFinancial.getLiabilityExpr(delegator),
226                         UtilFinancial.getEquityExpr(delegator)), EntityOperator.OR)),
227                      EntityOperator.AND);
228             List JavaDoc selectedFields = UtilMisc.toList("glAccountId", "glAccountTypeId", "glAccountClassId", "accountName", "postedDebits", "postedCredits");
229             selectedFields.add("endingBalance");
230             List JavaDoc accounts = delegator.findByCondition("GlAccountAndHistory", conditions, selectedFields, UtilMisc.toList("glAccountId"));
231
232             // now, create the separate account balance Maps and see if this period has been closed.
233
// if the period has been closed, then just get the accounts' balances from the endingBalance
234
// otherwise, get it by calculating the net of posted debits and credits
235
Map JavaDoc assetAccountBalances = new HashMap JavaDoc();
236             Map JavaDoc liabilityAccountBalances = new HashMap JavaDoc();
237             Map JavaDoc equityAccountBalances = new HashMap JavaDoc();
238
239             for (Iterator JavaDoc ai = accounts.iterator(); ai.hasNext(); ) {
240                 GenericValue account = (GenericValue) ai.next();
241                 double balance = 0.0;
242                 if (!isClosed) {
243                     // has not been closed - net of debit minus credit for debit accounts, credit minus debit for credit accounts
244
Double JavaDoc netBalance = UtilAccounting.getNetBalance(account, module);
245                    if (netBalance != null) {
246                        balance = netBalance.doubleValue();
247                    } else {
248                        return ServiceUtil.returnError("Cannot get a net balance for " + account);
249                    }
250                 } else {
251                     // has been closed, use endingBalance
252
balance = account.getDouble("endingBalance").doubleValue();
253                 }
254                 // classify and put into the appropriate Map
255
if (UtilAccounting.isAssetAccount(account)) {
256                    assetAccountBalances.put(account.getRelatedOne("GlAccount"), new Double JavaDoc(balance));
257                 } else if (UtilAccounting.isLiabilityAccount(account)) {
258                    liabilityAccountBalances.put(account.getRelatedOne("GlAccount"), new Double JavaDoc(balance));
259                 } else if (UtilAccounting.isEquityAccount(account)) {
260                    equityAccountBalances.put(account.getRelatedOne("GlAccount"), new Double JavaDoc(balance));
261                 }
262             }
263
264             // not closed. need to get a provisional net income and add it to p&l account
265
GenericValue retainedEarningsGlAccount = null;
266             Double JavaDoc interimNetIncome = null;
267             if (!isClosed) {
268                 // calculate provisional income
269
Map JavaDoc tmpResult = dispatcher.runSync("getActualNetIncomeSinceLastClosing", UtilMisc.toMap("organizationPartyId", organizationPartyId, "thruDate",
270                         UtilDateTime.toTimestamp(currentTimePeriod.getDate("thruDate")), "userLogin", userLogin));
271                 if (tmpResult.get("netIncome") == null) {
272                     return tmpResult;
273                 } else {
274                     // put provisional net income into the equity accounts Map
275
interimNetIncome = (Double JavaDoc) tmpResult.get("netIncome");
276                     retainedEarningsGlAccount = (GenericValue) tmpResult.get("retainedEarningsGlAccount");
277                     UtilMisc.addToDoubleInMap(equityAccountBalances, retainedEarningsGlAccount, interimNetIncome);
278                 }
279             }
280
281             // all done
282
Map JavaDoc result = ServiceUtil.returnSuccess();
283             result.put("assetAccountBalances", assetAccountBalances);
284             result.put("liabilityAccountBalances", liabilityAccountBalances);
285             result.put("equityAccountBalances", equityAccountBalances);
286             result.put("isClosed", new Boolean JavaDoc(isClosed));
287             result.put("retainedEarningsGlAccount", retainedEarningsGlAccount);
288             result.put("interimNetIncomeAmount", interimNetIncome);
289             return result;
290         } catch (GenericEntityException ex) {
291             return(ServiceUtil.returnError(ex.getMessage()));
292         } catch (GenericServiceException ex) {
293             return(ServiceUtil.returnError(ex.getMessage()));
294         }
295     }
296
297     /**
298      * Generates balance sheet as of a particular date, by working forward from the last closed time period,
299      * or, if none, from the beginning of the earliest time period.
300      */

301     
302     public static Map JavaDoc getBalanceSheetForDate(DispatchContext dctx, Map JavaDoc context) {
303         LocalDispatcher dispatcher = dctx.getDispatcher();
304         GenericDelegator delegator = dctx.getDelegator();
305         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
306         Timestamp JavaDoc asOfDate = (Timestamp JavaDoc) context.get("asOfDate");
307         GenericValue userLogin = (GenericValue) context.get("userLogin");
308         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
309         // default to generating "ACTUAL" balance sheets
310
if ((glFiscalTypeId == null) || (glFiscalTypeId.equals(""))) {
311             glFiscalTypeId = "ACTUAL";
312         }
313         
314         // balances of the asset, liability, and equity GL accounts, initially empty
315
Map JavaDoc assetAccountBalances = new HashMap JavaDoc();
316         Map JavaDoc liabilityAccountBalances = new HashMap JavaDoc();
317         Map JavaDoc equityAccountBalances = new HashMap JavaDoc();
318
319         try {
320 // find the last closed time period
321
Map JavaDoc tmpResult = dispatcher.runSync("findLastClosedDate", UtilMisc.toMap("organizationPartyId", organizationPartyId, "findDate", asOfDate, "userLogin", userLogin));
322
323             // figure the date and the last closed time period
324
Timestamp JavaDoc lastClosedDate = null;
325             GenericValue lastClosedTimePeriod = null;
326             if ((tmpResult == null) || (tmpResult.get("lastClosedDate") == null)) {
327                 return ServiceUtil.returnError("Cannot get a closed time period before " + asOfDate);
328             } else {
329                 lastClosedDate = (Timestamp JavaDoc) tmpResult.get("lastClosedDate");
330             }
331             if (tmpResult.get("lastClosedTimePeriod") != null) {
332                 lastClosedTimePeriod = (GenericValue) tmpResult.get("lastClosedTimePeriod");
333             }
334
335             // if there was a previously closed time period, then get a balance sheet as of the end of that time period. This balance sheet is our starting point
336
if (tmpResult.get("lastClosedTimePeriod") != null) {
337                 tmpResult = dispatcher.runSync("getBalanceSheetForTimePeriod", UtilMisc.toMap("organizationPartyId", organizationPartyId,
338                         "customTimePeriodId", lastClosedTimePeriod.getString("customTimePeriodId"), "userLogin", userLogin));
339                 if (tmpResult != null) {
340                     assetAccountBalances = (Map JavaDoc) tmpResult.get("assetAccountBalances");
341                     liabilityAccountBalances = (Map JavaDoc) tmpResult.get("liabilityAccountBalances");
342                     equityAccountBalances = (Map JavaDoc) tmpResult.get("equityAccountBalances");
343                 }
344             }
345
346             // now add the new asset, liability, and equity transactions
347
assetAccountBalances = getAcctgTransAndEntriesForClass(assetAccountBalances, organizationPartyId, lastClosedDate, asOfDate, glFiscalTypeId, "ASSET", userLogin, dispatcher);
348             liabilityAccountBalances = getAcctgTransAndEntriesForClass(liabilityAccountBalances, organizationPartyId, lastClosedDate, asOfDate, glFiscalTypeId, "LIABILITY", userLogin, dispatcher);
349             equityAccountBalances = getAcctgTransAndEntriesForClass(equityAccountBalances, organizationPartyId, lastClosedDate, asOfDate, glFiscalTypeId, "EQUITY", userLogin, dispatcher);
350
351             // calculate a net income since the last closed date and add it to our equity account balances
352
tmpResult = dispatcher.runSync("getIncomeStatementByDates", UtilMisc.toMap("organizationPartyId", organizationPartyId, "fromDate", lastClosedDate,
353                     "glFiscalTypeId", glFiscalTypeId, "thruDate", asOfDate, "userLogin", userLogin));
354             GenericValue retainedEarningsGlAccount = (GenericValue) tmpResult.get("retainedEarningsGlAccount");
355             Double JavaDoc interimNetIncome = (Double JavaDoc) tmpResult.get("netIncome");
356             UtilMisc.addToDoubleInMap(equityAccountBalances, retainedEarningsGlAccount, interimNetIncome);
357
358             // TODO: This is just copied over from getIncomeStatementByDates for now. We should implement a good version at some point.
359
boolean isClosed = true;
360             EntityConditionList conditions = new EntityConditionList(UtilMisc.toList(
361                new EntityExpr("organizationPartyId", EntityOperator.EQUALS, organizationPartyId),
362                new EntityExpr("isClosed", EntityOperator.NOT_EQUAL, "Y"),
363                new EntityConditionList(UtilMisc.toList(
364                    new EntityExpr("fromDate", EntityOperator.GREATER_THAN_EQUAL_TO, lastClosedDate),
365                    new EntityExpr("thruDate", EntityOperator.LESS_THAN_EQUAL_TO, asOfDate)), EntityOperator.OR)), EntityOperator.AND);
366             List JavaDoc timePeriods = delegator.findByCondition("CustomTimePeriod", conditions, UtilMisc.toList("customTimePeriodId"), UtilMisc.toList("customTimePeriodId"));
367             if (timePeriods.size() > 0) {
368                 isClosed = false;
369             }
370             
371             // all done
372
Map JavaDoc result = ServiceUtil.returnSuccess();
373             result.put("assetAccountBalances", assetAccountBalances);
374             result.put("liabilityAccountBalances", liabilityAccountBalances);
375             result.put("equityAccountBalances", equityAccountBalances);
376             result.put("isClosed", new Boolean JavaDoc(isClosed));
377             result.put("retainedEarningsGlAccount", retainedEarningsGlAccount);
378             result.put("interimNetIncomeAmount", interimNetIncome);
379             return(result);
380         } catch (GenericEntityException ex) {
381             return(ServiceUtil.returnError(ex.getMessage()));
382         } catch (GenericServiceException ex) {
383             return(ServiceUtil.returnError(ex.getMessage()));
384         }
385         
386     }
387
388     /**
389      * Finds and returns a List of AcctgTransAndEntries based on organizationPartyId, fromDate, thruDate, fiscalTypeId,
390      * subject to the glAccountClassIds in the glAccountClasses List.
391      */

392     public static Map JavaDoc getAcctgTransAndEntriesByType(DispatchContext dctx, Map JavaDoc context) {
393         GenericDelegator delegator = dctx.getDelegator();
394         Timestamp JavaDoc fromDate = (Timestamp JavaDoc) context.get("fromDate");
395         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
396         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
397         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
398         List JavaDoc glAccountClasses = (List JavaDoc) context.get("glAccountClasses");
399         String JavaDoc productId = (String JavaDoc) context.get("productId");
400         String JavaDoc partyId = (String JavaDoc) context.get("partyId");
401         
402         try {
403             // build a condition list of all the GlAccountClasses considered
404
List JavaDoc glAccountClassesConsidered = new ArrayList JavaDoc();
405             for (Iterator JavaDoc gACi = glAccountClasses.iterator(); gACi.hasNext(); ) {
406                 String JavaDoc glAccountClassId = (String JavaDoc) gACi.next();
407                 glAccountClassesConsidered.add(UtilFinancial.getGlAccountClassExpr(glAccountClassId, delegator));
408             }
409             
410             // find all accounting transaction entries for this organizationPartyId and falling into this time period which are
411
// of the specified types. Note we are only getting posted transactions here. This might change at some point.
412
List JavaDoc searchConditions = UtilMisc.toList(
413                     new EntityExpr("organizationPartyId", EntityOperator.EQUALS, organizationPartyId),
414                     new EntityExpr("isPosted", EntityOperator.EQUALS, "Y"),
415                     new EntityExpr("glFiscalTypeId", EntityOperator.EQUALS, glFiscalTypeId),
416                     new EntityConditionList(glAccountClassesConsidered, EntityOperator.OR),
417                     new EntityExpr("transactionDate", EntityOperator.GREATER_THAN_EQUAL_TO, fromDate),
418                     new EntityExpr("transactionDate", EntityOperator.LESS_THAN_EQUAL_TO, thruDate));
419             if (UtilValidate.isNotEmpty(productId)) {
420                 searchConditions.add(new EntityExpr("productId", EntityOperator.EQUALS, productId));
421             }
422             if (UtilValidate.isNotEmpty(partyId)) {
423                 searchConditions.add(new EntityExpr("partyId", EntityOperator.EQUALS, partyId));
424             }
425             EntityConditionList conditions = new EntityConditionList(searchConditions, EntityOperator.AND);
426             
427             List JavaDoc fieldsToGet = UtilMisc.toList("acctgTransId", "acctgTransTypeId", "acctgTransEntrySeqId", "glAccountId", "glAccountClassId", "amount");
428             fieldsToGet.add("debitCreditFlag");
429             fieldsToGet.add("productId");
430             fieldsToGet.add("partyId");
431             
432             List JavaDoc transactionEntries = delegator.findByCondition("AcctgTransAndEntries", conditions,
433                     fieldsToGet, // get these fields
434
UtilMisc.toList("acctgTransId", "acctgTransEntrySeqId")); // order by these fields
435

436             Map JavaDoc result = ServiceUtil.returnSuccess();
437             result.put("transactionEntries", transactionEntries);
438             return(result);
439         } catch (GenericEntityException ex) {
440             return(ServiceUtil.returnError(ex.getMessage()));
441         }
442     }
443     
444     /**
445      * gets a Map of glAccount -> sum of transactions for all income statement accounts (REVENUE, EXPENSE, INCOME) over a period of dates for an organization.
446      */

447     public static Map JavaDoc getIncomeStatementAccountSumsByDate(DispatchContext dctx, Map JavaDoc context) {
448         LocalDispatcher dispatcher = dctx.getDispatcher();
449         GenericDelegator delegator = dctx.getDelegator();
450         Timestamp JavaDoc fromDate = (Timestamp JavaDoc) context.get("fromDate");
451         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
452         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
453         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
454         GenericValue userLogin = (GenericValue) context.get("userLogin");
455     
456         try {
457             // first find all accounting transaction entries for this organizationPartyId and following into this time period which are
458
// income statement transactions (REVENUE, EXPENSE, INCOME).
459
List JavaDoc transactionEntries = null;
460             Map JavaDoc tmpResult = dispatcher.runSync("getAcctgTransAndEntriesByType", UtilMisc.toMap("organizationPartyId", organizationPartyId, "fromDate", fromDate,
461                     "thruDate", thruDate, "glFiscalTypeId", glFiscalTypeId, "glAccountClasses", UtilMisc.toList("REVENUE", "EXPENSE", "INCOME"), "userLogin", userLogin));
462             if ((tmpResult != null) && (tmpResult.get("transactionEntries") != null)) {
463                 transactionEntries = (List JavaDoc) tmpResult.get("transactionEntries");
464             }
465             // very important - we do not want the PERIOD_CLOSING transactions as this will duplicate the net income
466
transactionEntries = EntityUtil.filterOutByCondition(transactionEntries, new EntityExpr("acctgTransTypeId", EntityOperator.EQUALS, "PERIOD_CLOSING"));
467             
468             // now add them up by account. since we may have to deal with flipping the signs of transactions, we'd have analysis type of
469
// transaction (debit/credit) vs class of account (debit/credit), so it wasn't possible to just use a view-entity to sum it all up
470
Map JavaDoc glAccountSums = new HashMap JavaDoc();
471             tmpResult = dispatcher.runSync("addToAccountBalances", UtilMisc.toMap("glAccountSums", glAccountSums, "transactionEntries", transactionEntries,
472                     "userLogin", userLogin));
473             if (tmpResult.get("glAccountSums") == null) {
474                 return ServiceUtil.returnError("Cannot sum up account balances properly for income statement");
475             } else {
476                 Map JavaDoc result = ServiceUtil.returnSuccess();
477                 result.put("glAccountSums", glAccountSums);
478                 return result;
479             }
480         } catch (GenericServiceException ex) {
481             return(ServiceUtil.returnError(ex.getMessage()));
482         }
483     }
484     
485     /**
486      * Takes an initial Map of GlAccount, sums and a List of AcctgTransAndEntries and adds them to the Map,
487      * based on debit/credit flag of transaction transactionEntry and whether the account is a debit or credit account.
488      * Useful for doing income statement and intra-time-period updating of balance sheets, etc. */

489     public static Map JavaDoc addToAccountBalances(DispatchContext dctx, Map JavaDoc context) {
490         LocalDispatcher dispatcher = dctx.getDispatcher();
491         Map JavaDoc glAccountSums = (Map JavaDoc) context.get("glAccountSums");
492         List JavaDoc transactionEntries = (List JavaDoc) context.get("transactionEntries");
493
494         /* for each transaction entry, determine if the amount to be added needs to have its sign reversed, which is the
495          * case if the account and the debit/credit flag are opposite (ie, debit to a credit account or credit to a debit account.)
496          * then, add that amount to the glAccountSums map. Note that the key of glAccountSums is actually the GlAccount entity,
497          * not a glAccountId.
498          */

499         try {
500             for (Iterator JavaDoc tEi = transactionEntries.iterator(); tEi.hasNext(); ) {
501                 GenericValue transactionEntry = (GenericValue) tEi.next();
502                 GenericValue account = transactionEntry.getRelatedOne("GlAccount");
503
504                 double amountToAdd = transactionEntry.getDouble("amount").doubleValue();
505                 if ((UtilAccounting.isDebitAccount(account) && (transactionEntry.getString("debitCreditFlag").equals("C"))) ||
506                     (UtilAccounting.isCreditAccount(account) && (transactionEntry.getString("debitCreditFlag").equals("D")))) {
507                     amountToAdd = -amountToAdd;
508                 }
509                 
510                 UtilMisc.addToDoubleInMap(glAccountSums, transactionEntry.getRelatedOne("GlAccount"), new Double JavaDoc(amountToAdd));
511             }
512             Map JavaDoc result = ServiceUtil.returnSuccess();
513             result.put("glAccountSums", glAccountSums);
514             return result;
515         } catch (GenericEntityException ex) {
516             return(ServiceUtil.returnError(ex.getMessage()));
517         }
518     }
519
520
521     /**
522      * Little method to help out getBalanceSheetForDate. Gets all AcctgTransAndEntries of a glAccountClassId and add them to the
523      * original accountBalances Map, which is returned.
524      * @param accountBalances
525      * @param organizationPartyId
526      * @param fromDate
527      * @param thruDate
528      * @param glFiscalTypeId
529      * @param glAccountClassId
530      * @param userLogin
531      * @return
532      */

533     private static Map JavaDoc getAcctgTransAndEntriesForClass(Map JavaDoc accountBalances, String JavaDoc organizationPartyId, Timestamp JavaDoc fromDate,
534             Timestamp JavaDoc thruDate, String JavaDoc glFiscalTypeId, String JavaDoc glAccountClassId, GenericValue userLogin, LocalDispatcher dispatcher) {
535         try {
536             // first get all the AcctgTransAndEntries of this glAccountClassId
537
Map JavaDoc tmpResult = dispatcher.runSync("getAcctgTransAndEntriesByType", UtilMisc.toMap("organizationPartyId", organizationPartyId,
538                     "fromDate", fromDate, "thruDate", thruDate, "glFiscalTypeId", glFiscalTypeId, "glAccountClasses", UtilMisc.toList(glAccountClassId), "userLogin", userLogin));
539             List JavaDoc transactionEntries = (List JavaDoc) tmpResult.get("transactionEntries");
540             
541             // now add it to accountBalances
542
tmpResult = dispatcher.runSync("addToAccountBalances", UtilMisc.toMap("glAccountSums", accountBalances, "transactionEntries", transactionEntries, "userLogin", userLogin));
543             accountBalances = (Map JavaDoc) tmpResult.get("glAccountSums");
544             return accountBalances;
545         } catch (GenericServiceException ex) {
546             Debug.logError(ex.getMessage(), module);
547             return null;
548         }
549     }
550
551     public static Map JavaDoc getComparativeBalanceSheet(DispatchContext dctx, Map JavaDoc context) {
552         LocalDispatcher dispatcher = dctx.getDispatcher();
553         GenericDelegator delegator = dctx.getDelegator();
554
555         // input parameters
556
String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
557         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
558         Timestamp JavaDoc fromDate = (Timestamp JavaDoc) context.get("fromDate");
559         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
560         GenericValue userLogin = (GenericValue) context.get("userLogin");
561
562         // validate the from/thru date
563
if (fromDate.after(thruDate)) {
564             return ServiceUtil.returnError("Cannot create a comparative balance for a from date that is after the thru date!");
565         }
566
567         try {
568             // create the balance sheet for the fromDate
569
Map JavaDoc input = UtilMisc.toMap("organizationPartyId", organizationPartyId, "glFiscalTypeId", glFiscalTypeId, "userLogin", userLogin);
570             input.put("asOfDate", fromDate);
571             Map JavaDoc fromDateResults = dispatcher.runSync("getBalanceSheetForDate", input);
572             if (ServiceUtil.isError(fromDateResults)) {
573                 return ServiceUtil.returnError("Failed to create comparative balance sheet.", null, null, fromDateResults);
574             }
575
576             // create the balance sheet for the thruDate
577
input.put("asOfDate", thruDate);
578             Map JavaDoc thruDateResults = dispatcher.runSync("getBalanceSheetForDate", input);
579             if (ServiceUtil.isError(thruDateResults)) {
580                 return ServiceUtil.returnError("Failed to create comparative balance sheet.", null, null, thruDateResults);
581             }
582
583             Map JavaDoc results = ServiceUtil.returnSuccess();
584
585             // include the two balance sheets in the results
586
results.put("fromDateAccountBalances", fromDateResults);
587             results.put("thruDateAccountBalances", thruDateResults);
588
589             // compute the balance difference for each type of account
590
results.put("liabilityAccountBalances",
591                     calculateDifferenceBalance((Map JavaDoc) fromDateResults.get("liabilityAccountBalances"), (Map JavaDoc) thruDateResults.get("liabilityAccountBalances")));
592             results.put("assetAccountBalances",
593                     calculateDifferenceBalance((Map JavaDoc) fromDateResults.get("assetAccountBalances"), (Map JavaDoc) thruDateResults.get("assetAccountBalances")));
594             results.put("equityAccountBalances",
595                     calculateDifferenceBalance((Map JavaDoc) fromDateResults.get("equityAccountBalances"), (Map JavaDoc) thruDateResults.get("equityAccountBalances")));
596             return results;
597         } catch (GenericServiceException e) {
598             Debug.logError(e, "Failed to compute comparative balance: " + e.getMessage(), module);
599             return ServiceUtil.returnError("Failed to compute comparative balance: " + e.getMessage());
600         }
601     }
602
603     /**
604      * Calculates the difference between the two balances for the input maps of {account, fromDateBalance} and {account, thruDateBalance}
605      */

606     private static Map JavaDoc calculateDifferenceBalance(Map JavaDoc fromDateMap, Map JavaDoc thruDateMap) {
607         Map JavaDoc resultMap = FastMap.newInstance();
608
609         // first iteratee through the thru date accounts
610
for (Iterator JavaDoc iter = thruDateMap.keySet().iterator(); iter.hasNext(); ) {
611             GenericValue account = (GenericValue) iter.next();
612             Double JavaDoc thruDateBalance = (Double JavaDoc) thruDateMap.get(account);
613             Double JavaDoc fromDateBalance = (Double JavaDoc) fromDateMap.get(account);
614             if (thruDateBalance == null) thruDateBalance = new Double JavaDoc(0);
615             if (fromDateBalance == null) fromDateBalance = new Double JavaDoc(0);
616             double difference = thruDateBalance.doubleValue() - fromDateBalance.doubleValue();
617             resultMap.put(account, (new BigDecimal JavaDoc(difference)).setScale(decimals, rounding));
618         }
619
620         // iterate through the from date accounts that were missed because no thru date account exists
621
for (Iterator JavaDoc iter = fromDateMap.keySet().iterator(); iter.hasNext(); ) {
622             GenericValue account = (GenericValue) iter.next();
623             if (resultMap.get(account) != null) continue; // already have a balance
624
Double JavaDoc fromDateBalance = (Double JavaDoc) fromDateMap.get(account);
625             if (fromDateBalance == null) fromDateBalance = new Double JavaDoc(0);
626             double difference = 0 - fromDateBalance.doubleValue();
627             resultMap.put(account, (new BigDecimal JavaDoc(difference)).setScale(decimals, rounding));
628         }
629
630         return resultMap;
631     }
632
633     /**
634      * Helper method to transform input from and thru time periods to fromDate and thruDates. It will then call
635      * the given service with the given input map and handle service/entity exceptions.
636      */

637     private static Map JavaDoc reportServiceTimePeriodHelper(DispatchContext dctx, Map JavaDoc context, String JavaDoc serviceName, Map JavaDoc input) {
638         LocalDispatcher dispatcher = dctx.getDispatcher();
639         GenericDelegator delegator = dctx.getDelegator();
640         String JavaDoc fromTimePeriodId = (String JavaDoc) context.get("fromTimePeriodId");
641         String JavaDoc thruTimePeriodId = (String JavaDoc) context.get("thruTimePeriodId");
642         String JavaDoc organizationPartyId = (String JavaDoc) input.get("organizationPartyId");
643         
644         try {
645             // get the right start and ending custom time periods
646
GenericValue fromTimePeriod = delegator.findByPrimaryKey("CustomTimePeriod", UtilMisc.toMap("customTimePeriodId", fromTimePeriodId));
647             GenericValue thruTimePeriod = delegator.findByPrimaryKey("CustomTimePeriod", UtilMisc.toMap("customTimePeriodId", thruTimePeriodId));
648
649             // make sure these time periods belong to the organization and the from and thru dates are there
650
if (fromTimePeriod == null) {
651                 return ServiceUtil.returnError("Custom time period " + fromTimePeriodId + " does not exist");
652             }
653             if (thruTimePeriod == null) {
654                 return ServiceUtil.returnError("Custom time period " + thruTimePeriodId + " does not exist");
655             }
656             if (!(fromTimePeriod.getString("organizationPartyId").equals(organizationPartyId))) {
657                 return ServiceUtil.returnError("Custom time period " + fromTimePeriodId + " does not belong to " + organizationPartyId);
658             }
659             if (!(thruTimePeriod.getString("organizationPartyId").equals(organizationPartyId))) {
660                 return ServiceUtil.returnError("Custom time period " + thruTimePeriodId + " does not belong to " + organizationPartyId);
661             }
662             if (fromTimePeriod.get("fromDate") == null) {
663                 return ServiceUtil.returnError("Cannot get a starting date from custom time period = " + fromTimePeriodId);
664             } else if (thruTimePeriod.get("thruDate") == null) {
665                 return ServiceUtil.returnError("Cannot get a starting date from custom time period = " + thruTimePeriodId);
666             }
667
668             // call the service and pass back the results.
669
input.put("fromDate", UtilDateTime.toTimestamp(fromTimePeriod.getDate("fromDate")));
670             input.put("thruDate", UtilDateTime.toTimestamp(thruTimePeriod.getDate("thruDate")));
671             Map JavaDoc result = dispatcher.runSync(serviceName, input);
672             return result;
673         } catch (GenericEntityException ex) {
674             return(ServiceUtil.returnError(ex.getMessage()));
675         } catch (GenericServiceException ex) {
676             return(ServiceUtil.returnError(ex.getMessage()));
677         }
678     }
679
680     public static Map JavaDoc getCashFlowStatementForTimePeriods(DispatchContext dctx, Map JavaDoc context) {
681         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
682         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
683         GenericValue userLogin = (GenericValue) context.get("userLogin");
684         Map JavaDoc input = UtilMisc.toMap("organizationPartyId", organizationPartyId, "glFiscalTypeId", glFiscalTypeId, "userLogin", userLogin);
685         return reportServiceTimePeriodHelper(dctx, context, "getCashFlowStatementForDates", input);
686     }
687
688     public static Map JavaDoc getCashFlowStatementForDates(DispatchContext dctx, Map JavaDoc context) {
689         LocalDispatcher dispatcher = dctx.getDispatcher();
690         GenericDelegator delegator = dctx.getDelegator();
691         GenericValue userLogin = (GenericValue) context.get("userLogin");
692
693         Timestamp JavaDoc fromDate = (Timestamp JavaDoc) context.get("fromDate");
694         Timestamp JavaDoc thruDate = (Timestamp JavaDoc) context.get("thruDate");
695         String JavaDoc organizationPartyId = (String JavaDoc) context.get("organizationPartyId");
696         String JavaDoc glFiscalTypeId = (String JavaDoc) context.get("glFiscalTypeId");
697
698         try {
699             Map JavaDoc input = UtilMisc.toMap("organizationPartyId", organizationPartyId, "glFiscalTypeId", glFiscalTypeId, "userLogin", userLogin);
700
701             // run comparitive balance sheet for the date range
702
input.put("fromDate", fromDate);
703             input.put("thruDate", thruDate);
704             Map JavaDoc comparativeResults = dispatcher.runSync("getComparativeBalanceSheet", input);
705             if (ServiceUtil.isError(comparativeResults)) {
706                 return ServiceUtil.returnError("Faild to compute cash flow statement. ", null, null, comparativeResults);
707             }
708
709             // extract the from date balance sheet and thru date balance sheet for convenience
710
Map JavaDoc fromDateAccountBalances = (Map JavaDoc) comparativeResults.get("fromDateAccountBalances");
711             Map JavaDoc thruDateAccountBalances = (Map JavaDoc) comparativeResults.get("thruDateAccountBalances");
712
713             // run income statement for same date range
714
Map JavaDoc incomeResults = dispatcher.runSync("getIncomeStatementByDates", input);
715             if (ServiceUtil.isError(incomeResults)) {
716                 return ServiceUtil.returnError("Faild to compute cash flow statement. ", null, null, incomeResults);
717             }
718
719             // extract the netIncome for convenience
720
BigDecimal JavaDoc netIncome = new BigDecimal JavaDoc(((Double JavaDoc) incomeResults.get("netIncome")).doubleValue());
721
722             // compute the beginning cash amount from accounts with glAccountClassId = CASH_EQUIVALENT and make a map of these accounts for return
723
BigDecimal JavaDoc beginningCashAmount = ZERO;
724             Map JavaDoc beginningCashAccountBalances = FastMap.newInstance();
725             Map JavaDoc beginningSheet = (Map JavaDoc) fromDateAccountBalances.get("assetAccountBalances"); // beginning cash equivalents are in from date balance sheet assets
726
for (Iterator JavaDoc iter = beginningSheet.keySet().iterator(); iter.hasNext(); ) {
727                 GenericValue account = (GenericValue) iter.next();
728                 if (!UtilAccounting.isAccountClass(account, "CASH_EQUIVALENT")) continue; // skip non cash equivalent accounts
729
BigDecimal JavaDoc amount = new BigDecimal JavaDoc(((Double JavaDoc) beginningSheet.get(account)).doubleValue());
730                 beginningCashAccountBalances.put(account, amount);
731                 beginningCashAmount = beginningCashAmount.add(amount).setScale(decimals, rounding);
732             }
733
734             // compute the ending cash amount from accounts with glAccountClassId = CASH_EQUIVALENT and make a map of these accounts for return
735
BigDecimal JavaDoc endingCashAmount = ZERO;
736             Map JavaDoc endingCashAccountBalances = FastMap.newInstance();
737             Map JavaDoc endingSheet = (Map JavaDoc) thruDateAccountBalances.get("assetAccountBalances"); // ending cash equivalents are in thru date balance sheet assets
738
for (Iterator JavaDoc iter = endingSheet.keySet().iterator(); iter.hasNext(); ) {
739                 GenericValue account = (GenericValue) iter.next();
740                 if (!UtilAccounting.isAccountClass(account, "CASH_EQUIVALENT")) continue; // use our handy recursive method to test the class
741
BigDecimal JavaDoc amount = new BigDecimal JavaDoc(((Double JavaDoc) endingSheet.get(account)).doubleValue());
742                 endingCashAccountBalances.put(account, amount);
743                 endingCashAmount = endingCashAmount.add(amount).setScale(decimals, rounding);
744             }
745
746             // cash flow amounts
747
BigDecimal JavaDoc operatingCashFlow = ZERO;
748             BigDecimal JavaDoc investingCashFlow = ZERO;
749             BigDecimal JavaDoc financingCashFlow = ZERO;
750
751             // cash flow maps of accounts to amounts
752
Map JavaDoc operatingCashFlowAccountBalances = FastMap.newInstance();
753             Map JavaDoc investingCashFlowAccountBalances = FastMap.newInstance();
754             Map JavaDoc financingCashFlowAccountBalances = FastMap.newInstance();
755
756             // add net income to operating cash flow
757
operatingCashFlow = netIncome;
758             
759             // add non cash expense accounts to the operating cash flow
760
Map JavaDoc glAccountSums = (Map JavaDoc) incomeResults.get("glAccountSums");
761             if ((glAccountSums != null) && (glAccountSums.keySet() != null)) {
762                 for (Iterator JavaDoc iter = glAccountSums.keySet().iterator(); iter.hasNext(); ) {
763                     GenericValue account = (GenericValue) iter.next();
764                     if (UtilAccounting.isAccountClass(account, "NON_CASH_EXPENSE") && !UtilAccounting.isAccountClass(account, "INVENTORY_ADJUST")) {
765                         BigDecimal JavaDoc amount = new BigDecimal JavaDoc(((Double JavaDoc) glAccountSums.get(account)).doubleValue());
766                         operatingCashFlowAccountBalances.put(account, amount.negate());
767                         operatingCashFlow = operatingCashFlow.subtract(amount).setScale(decimals, rounding);
768                     }
769                 }
770             }
771             
772             // compute the cash flows from assets
773
Map JavaDoc statement = (Map JavaDoc) comparativeResults.get("assetAccountBalances");
774             if ((statement != null) && (statement.keySet() != null)) {
775                 for (Iterator JavaDoc iter = statement.keySet().iterator(); iter.hasNext(); ) {
776                     GenericValue account = (GenericValue) iter.next();
777
778                     // get current assets that are not cash equivalent accounts and flip the sign of the amounts, then add to operating cash flow
779
if (UtilAccounting.isAccountClass(account, "CURRENT_ASSET") && !UtilAccounting.isAccountClass(account, "CASH_EQUIVALENT")) {
780                         BigDecimal JavaDoc amount = (BigDecimal JavaDoc) statement.get(account);
781                         amount = ZERO.subtract(amount).setScale(decimals, rounding); // flip the sign and use this value
782
operatingCashFlowAccountBalances.put(account, amount);
783                         operatingCashFlow = operatingCashFlow.add(amount).setScale(decimals, rounding);
784                     }
785                     
786                     // add to investing cash flow any long term assets
787
else if (UtilAccounting.isAccountClass(account, "LONGTERM_ASSET") &&
788                              !UtilAccounting.isAccountClass(account, "ACCUM_DEPRECIATION") &&
789                              !UtilAccounting.isAccountClass(account, "ACCUM_AMORTIZATION")) {
790                         BigDecimal JavaDoc amount = (BigDecimal JavaDoc) statement.get(account);
791                         investingCashFlowAccountBalances.put(account, amount.negate());
792                         investingCashFlow = investingCashFlow.subtract((BigDecimal JavaDoc) statement.get(account)).setScale(decimals, rounding);
793                     }
794                 }
795             }
796             
797             // compute the cash flows from liabilities
798
statement = (Map JavaDoc) comparativeResults.get("liabilityAccountBalances");
799             if ((statement != null) && (statement.keySet() != null)) {
800                 for (Iterator JavaDoc iter = statement.keySet().iterator(); iter.hasNext(); ) {
801                     GenericValue account = (GenericValue) iter.next();
802
803                     // add to operating cash flow any current liabilities
804
if (UtilAccounting.isAccountClass(account, "CURRENT_LIABILITY")) {
805                         operatingCashFlowAccountBalances.put(account, statement.get(account));
806                         operatingCashFlow = operatingCashFlow.add((BigDecimal JavaDoc) statement.get(account)).setScale(decimals, rounding);
807                     }
808
809                     // add to financing cash flow any long term liabilities
810
else if (UtilAccounting.isAccountClass(account, "LONGTERM_LIABILITY")) {
811                         financingCashFlowAccountBalances.put(account, statement.get(account));
812                         financingCashFlow = financingCashFlow.add((BigDecimal JavaDoc) statement.get(account)).setScale(decimals, rounding);
813                     }
814                 }
815             }
816
817             // compute the cash flows from equity (sans the retained earnings accounts)
818
statement = (Map JavaDoc) comparativeResults.get("equityAccountBalances");
819             if ((statement != null) && (statement.keySet() != null)) {
820                 for (Iterator JavaDoc iter = statement.keySet().iterator(); iter.hasNext(); ) {
821                     GenericValue account = (GenericValue) iter.next();
822
823                     // add all equity to financing cash flow
824
if (UtilAccounting.isAccountClass(account, "OWNERS_EQUITY")) {
825                         financingCashFlowAccountBalances.put(account, statement.get(account));
826                         financingCashFlow = financingCashFlow.add((BigDecimal JavaDoc) statement.get(account)).setScale(decimals, rounding);
827                     }
828                 }
829             }
830             
831             // handle DISTRIBUTION account transactions
832
statement = new HashMap JavaDoc(); // need to clear it now because this method adds the new DISTRIBUTION accounts and sums to the Map
833
statement = getAcctgTransAndEntriesForClass(statement, organizationPartyId,
834                     fromDate, thruDate, glFiscalTypeId, "DISTRIBUTION", userLogin, dispatcher);
835             if ((statement != null) && (statement.keySet() != null)) {
836                 for (Iterator JavaDoc iter = statement.keySet().iterator(); iter.hasNext(); ) {
837                     GenericValue account = (GenericValue) iter.next();
838                     if (statement.get(account) != null) {
839                         BigDecimal JavaDoc amount = new BigDecimal JavaDoc(((Double JavaDoc) statement.get(account)).doubleValue());
840                         financingCashFlowAccountBalances.put(account, amount.negate());
841                         financingCashFlow = financingCashFlow.subtract(amount).setScale(decimals, rounding);
842                     }
843                 }
844             }
845             
846             // complementary validation that the net cash flow is equal for the two methods to compute it
847
BigDecimal JavaDoc netCashFlowOne = endingCashAmount.subtract(beginningCashAmount);
848             BigDecimal JavaDoc netCashFlowTwo = operatingCashFlow.add(investingCashFlow).add(financingCashFlow);
849             if (netCashFlowOne.compareTo(netCashFlowTwo) != 0) {
850                 Debug.logWarning("Net cash flow computation yielded different values! (ending cash amount - beginning cash amount) = ["
851                         + netCashFlowOne.toString() + "]; (operating + investing + financing) = [" + netCashFlowTwo.toString() + "]", module);
852             }
853
854             Map JavaDoc results = ServiceUtil.returnSuccess();
855             results.put("beginningCashAmount", beginningCashAmount);
856             results.put("beginningCashAccountBalances", beginningCashAccountBalances);
857             results.put("endingCashAmount", endingCashAmount);
858             results.put("endingCashAccountBalances", endingCashAccountBalances);
859             results.put("operatingCashFlowAccountBalances", operatingCashFlowAccountBalances);
860             results.put("investingCashFlowAccountBalances", investingCashFlowAccountBalances);
861             results.put("financingCashFlowAccountBalances", financingCashFlowAccountBalances);
862             results.put("operatingCashFlow", operatingCashFlow);
863             results.put("investingCashFlow", investingCashFlow);
864             results.put("financingCashFlow", financingCashFlow);
865             results.put("netIncome", netIncome);
866             results.put("netCashFlow", netCashFlowTwo); // return (operating + investing + financing) as the net cash flow
867
return results;
868         } catch (GenericServiceException e) {
869             Debug.logError(e, "Failed to compute cash flow statement: " + e.getMessage(), module);
870             return ServiceUtil.returnError("Failed to compute cash flow statement: " + e.getMessage());
871         } catch (GenericEntityException e) {
872             Debug.logError(e, "Failed to compute cash flow statement: " + e.getMessage(), module);
873             return ServiceUtil.returnError("Failed to compute cash flow statement: " + e.getMessage());
874         }
875     }
876 }
877
Popular Tags