001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.bc.document.dataaccess.impl;
017    
018    import java.math.BigDecimal;
019    import java.util.ArrayList;
020    
021    import org.kuali.kfs.module.bc.BCConstants;
022    import org.kuali.kfs.module.bc.batch.dataaccess.impl.SQLForStep;
023    import org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionReasonStatisticsReportDao;
024    import org.kuali.rice.kns.service.PersistenceService;
025    import org.kuali.rice.kns.util.Guid;
026    import org.kuali.rice.kns.util.KualiDecimal;
027    
028    /**
029     * build the set of rows for the salary reason statistics report
030     */
031    
032    public class BudgetConstructionReasonStatisticsReportDaoJdbc extends BudgetConstructionDaoJdbcBase implements BudgetConstructionReasonStatisticsReportDao {
033        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetConstructionReasonStatisticsReportDaoJdbc.class);
034    
035        private static ArrayList<SQLForStep> updateReportsReasonStatisticsTable    = new ArrayList<SQLForStep>(10);
036        private static ArrayList<SQLForStep> reportReasonStatisticsWithThreshold   = new ArrayList<SQLForStep>(3);
037        private static ArrayList<SQLForStep> reportReasonStatisticsWithNoThreshold = new ArrayList<SQLForStep>(2);
038        
039        private PersistenceService persistenceService;
040    
041        public BudgetConstructionReasonStatisticsReportDaoJdbc() {
042    
043            ArrayList<Integer> insertionPoints = new ArrayList<Integer>(10);
044            
045            // builds and updates ReasonStatisticsReports
046            
047            /* get all emplids for the selections if we are doing the report using a threshold*/
048            StringBuilder sqlText = new StringBuilder(2500);
049            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT01_MT (SESID, EMPLID) \n");
050            sqlText.append("SELECT DISTINCT ?, bcaf.emplid \n");
051            sqlText.append("FROM LD_BCN_CTRL_LIST_T ctrl, LD_PNDBC_APPTFND_T bcaf, LD_BCN_OBJ_PICK_T pick \n");
052            sqlText.append("WHERE ctrl.person_unvl_id = ? \n");
053            sqlText.append(" AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
054            sqlText.append(" AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
055            sqlText.append(" AND bcaf.account_nbr = ctrl.account_nbr \n");
056            sqlText.append(" AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
057            sqlText.append(" AND bcaf.emplid <> '");
058            // employee ID for a vacant line in budget construction
059            insertionPoints.add(sqlText.length());
060            sqlText.append("' \n");
061            sqlText.append(" AND bcaf.fin_object_cd = pick.fin_object_cd \n");
062            sqlText.append(" AND pick.person_unvl_id = ctrl.person_unvl_id \n");
063            sqlText.append(" AND pick.select_flag > 0 \n");
064                      
065            reportReasonStatisticsWithThreshold.add(new SQLForStep(sqlText,insertionPoints));
066            sqlText.delete(0, sqlText.length());
067            insertionPoints.clear();
068            
069            /* get emplids with at least one reason rec for the selections if we are doing the report without a threshold*/
070            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT01_MT (SESID, EMPLID) \n");
071            sqlText.append("SELECT DISTINCT ?, bcaf.emplid \n");
072            sqlText.append("FROM LD_BCN_CTRL_LIST_T ctrl, LD_PNDBC_APPTFND_T bcaf, LD_BCN_OBJ_PICK_T pick, LD_BCN_AF_REASON_T reas, LD_BCN_RSN_CD_PK_T rpk \n");
073            sqlText.append("WHERE ctrl.person_unvl_id = ? \n");
074            sqlText.append(" AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
075            sqlText.append(" AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
076            sqlText.append(" AND bcaf.account_nbr = ctrl.account_nbr \n");
077            sqlText.append(" AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
078            sqlText.append(" AND bcaf.emplid <> '");
079            // employee ID for a vacant line in budget construction
080            insertionPoints.add(sqlText.length());
081            sqlText.append("' \n");
082            sqlText.append(" AND bcaf.fin_object_cd = pick.fin_object_cd \n");
083            sqlText.append(" AND pick.person_unvl_id = ctrl.person_unvl_id \n");
084            sqlText.append(" AND pick.select_flag > 0 \n");
085            sqlText.append(" AND bcaf.univ_fiscal_yr = reas.univ_fiscal_yr \n");
086            sqlText.append(" AND bcaf.fin_coa_cd = reas.fin_coa_cd \n");
087            sqlText.append(" AND bcaf.account_nbr = reas.account_nbr \n");
088            sqlText.append(" AND bcaf.sub_acct_nbr = reas.sub_acct_nbr \n");
089            sqlText.append(" AND bcaf.fin_object_cd = reas.fin_object_cd \n");
090            sqlText.append(" AND bcaf.fin_sub_obj_cd = reas.fin_sub_obj_cd \n");
091            sqlText.append(" AND bcaf.position_nbr = reas.position_nbr \n");
092            sqlText.append(" AND bcaf.emplid = reas.emplid \n");
093            sqlText.append(" AND reas.appt_fnd_reason_cd = rpk.appt_fnd_reason_cd \n");
094            sqlText.append(" AND rpk.person_unvl_id = ctrl.person_unvl_id \n");
095            sqlText.append(" AND rpk.select_flag <> 0 \n");
096                      
097            reportReasonStatisticsWithNoThreshold.add(new SQLForStep(sqlText,insertionPoints));
098            sqlText.delete(0, sqlText.length());
099            insertionPoints.clear();
100    
101            /* get the salary and months data for people not on leave from request, base (CSF), and the position table */
102            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT02_MT \n");
103            sqlText.append("(SESID, EMPLID, POSITION_NBR, SAL_AMT, SAL_PCT, SAL_MTHS,  \n");
104            sqlText.append(" POS_CSF_AMT, POS_CSF_TM_PCT, SAL_PMTHS) \n");
105            sqlText.append("SELECT ?, bcaf.emplid, bcaf.position_nbr, bcaf.appt_rqst_amt, bcaf.appt_rqst_tm_pct, bcaf.appt_fnd_mo,  \n");
106            sqlText.append(" COALESCE(bcsf.pos_csf_amt,0), COALESCE(bcsf.pos_csf_tm_pct,0.0), posn.iu_pay_months \n");
107            sqlText.append("FROM (LD_PNDBC_APPTFND_T bcaf LEFT OUTER JOIN LD_BCN_CSF_TRCKR_T bcsf \n");
108            sqlText.append(" ON ((bcaf.univ_fiscal_yr = bcsf.univ_fiscal_yr) \n");
109            sqlText.append(" AND (bcaf.fin_coa_cd = bcsf.fin_coa_cd) \n");
110            sqlText.append(" AND (bcaf.account_nbr = bcsf.account_nbr) \n");
111            sqlText.append(" AND (bcaf.sub_acct_nbr = bcsf.sub_acct_nbr) \n");
112            sqlText.append(" AND (bcaf.fin_object_cd = bcsf.fin_object_cd) \n");
113            sqlText.append(" AND (bcaf.fin_sub_obj_cd = bcsf.fin_sub_obj_cd) \n");
114            sqlText.append(" AND (bcaf.position_nbr = bcsf.position_nbr) \n");
115            sqlText.append(" AND (bcaf.emplid = bcsf.emplid))),\n");
116            sqlText.append(" LD_BCN_POS_T posn, LD_BCN_CTRL_LIST_T ctrl, LD_BCN_BUILD_EXSALTOT01_MT tssn, LD_BCN_OBJ_PICK_T pick \n");
117            sqlText.append("WHERE ctrl.person_unvl_id = ? \n");
118            sqlText.append(" AND tssn.sesid = ? \n");
119            sqlText.append(" AND bcaf.emplid = tssn.emplid \n");
120            sqlText.append(" AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
121            sqlText.append(" AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
122            sqlText.append(" AND bcaf.account_nbr = ctrl.account_nbr \n");
123            sqlText.append(" AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
124            sqlText.append(" AND bcaf.emplid <> '");
125            // employee ID for a vacant line in budget construction
126            insertionPoints.add(sqlText.length());
127            sqlText.append("' \n");
128            sqlText.append(" AND bcaf.appt_fnd_dur_cd = '");
129            // default funding duration code
130            insertionPoints.add(sqlText.length());
131            sqlText.append("'\n");
132            sqlText.append(" AND bcaf.fin_object_cd = pick.fin_object_cd \n");
133            sqlText.append(" AND pick.person_unvl_id = ctrl.person_unvl_id \n");
134            sqlText.append(" AND pick.select_flag > 0 \n");
135            sqlText.append(" AND bcaf.univ_fiscal_yr = posn.univ_fiscal_yr \n");
136            sqlText.append(" AND bcaf.position_nbr = posn.position_nbr \n");
137    
138            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText,insertionPoints));
139            sqlText.delete(0, sqlText.length());
140            insertionPoints.clear();
141            
142            /* get leave flagged bcaf, bcsf and posn info first */
143            /* uses leave related info from bcaf, etc */
144            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT02_MT \n");
145            sqlText.append("(SESID, EMPLID, POSITION_NBR, SAL_AMT, SAL_PCT, SAL_MTHS,  \n");
146            sqlText.append(" POS_CSF_AMT, POS_CSF_TM_PCT, SAL_PMTHS) \n");
147            sqlText.append(" SELECT ?, bcaf.emplid, bcaf.position_nbr, bcaf.appt_rqst_csf_amt, bcaf.appt_rqcsf_tm_pct, posn.iu_norm_work_months, \n");
148            sqlText.append(" bcsf.pos_csf_amt, bcsf.pos_csf_tm_pct, posn.iu_pay_months \n");
149            sqlText.append("FROM (LD_PNDBC_APPTFND_T bcaf LEFT OUTER JOIN LD_BCN_CSF_TRCKR_T bcsf \n");
150            sqlText.append("ON ((bcaf.univ_fiscal_yr = bcsf.univ_fiscal_yr) \n");
151            sqlText.append(" AND (bcaf.fin_coa_cd = bcsf.fin_coa_cd)\n");
152            sqlText.append(" AND (bcaf.account_nbr = bcsf.account_nbr) \n");
153            sqlText.append(" AND (bcaf.sub_acct_nbr = bcsf.sub_acct_nbr) \n");
154            sqlText.append(" AND (bcaf.fin_object_cd = bcsf.fin_object_cd) \n");
155            sqlText.append(" AND (bcaf.fin_sub_obj_cd = bcsf.fin_sub_obj_cd) \n");
156            sqlText.append(" AND (bcaf.position_nbr = bcsf.position_nbr) \n");
157            sqlText.append(" AND (bcaf.emplid = bcsf.emplid))),\n");
158            sqlText.append(" LD_BCN_POS_T posn, LD_BCN_CTRL_LIST_T ctrl, LD_BCN_BUILD_EXSALTOT01_MT tssn, LD_BCN_OBJ_PICK_T pick \n");
159            sqlText.append("WHERE ctrl.person_unvl_id = ? \n");
160            sqlText.append("AND tssn.sesid = ? \n");
161            sqlText.append("AND bcaf.emplid = tssn.emplid \n");
162            sqlText.append("AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
163            sqlText.append("AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
164            sqlText.append("AND bcaf.account_nbr = ctrl.account_nbr \n");
165            sqlText.append("AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
166            sqlText.append("AND bcaf.emplid <> '");
167            // employee ID for a vacant line in budget construction
168            insertionPoints.add(sqlText.length());
169            sqlText.append("' \n");
170            sqlText.append("AND bcaf.appt_fnd_dur_cd <> '");
171            // default funding duration code
172            insertionPoints.add(sqlText.length());
173            sqlText.append("' \n");
174            sqlText.append("AND bcaf.fin_object_cd = pick.fin_object_cd \n");
175            sqlText.append("AND pick.person_unvl_id = ctrl.person_unvl_id \n");
176            sqlText.append("AND pick.select_flag > 0 \n");
177            sqlText.append("AND bcaf.univ_fiscal_yr = posn.univ_fiscal_yr \n");
178            sqlText.append("AND bcaf.position_nbr = posn.position_nbr \n");
179                
180            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText,insertionPoints));
181            sqlText.delete(0, sqlText.length());
182            insertionPoints.clear();
183            
184            /* take the request appointment attributes (months and position months) from the row for each person with the largest request amount */
185            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT03_MT (SESID, EMPLID, SAL_MTHS, SAL_PMTHS) \n");
186            sqlText.append("SELECT DISTINCT ?, sd.emplid, sd.sal_mths, sd.sal_pmths \n");
187            sqlText.append("FROM LD_BCN_BUILD_EXSALTOT02_MT sd \n");
188            sqlText.append("WHERE sesid = ? \n");
189            sqlText.append(" AND sd.sal_amt <> 0 \n");
190            sqlText.append(" AND sd.sal_amt = \n");
191            sqlText.append("  (SELECT max(sd2.sal_amt) \n");
192            sqlText.append("  FROM LD_BCN_BUILD_EXSALTOT02_MT sd2 \n");
193            sqlText.append("  WHERE sd2.sesid = sd.sesid AND sd2.emplid = sd.emplid) \n");
194            sqlText.append(" AND sd.position_nbr = \n");
195            sqlText.append("  (SELECT min(sd3.position_nbr) \n");
196            sqlText.append("  FROM LD_BCN_BUILD_EXSALTOT02_MT sd3 \n");
197            sqlText.append("  WHERE sd3.sesid = sd.sesid AND sd3.emplid = sd.emplid AND sd3.sal_amt = sd.sal_amt) \n");
198            
199            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
200            sqlText.delete(0, sqlText.length());
201            
202            /* get the previous year's (base) appointment attributes for each person from the base row with the largest amount */
203            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT04_MT (SESID, EMPLID, CSF_MTHS, CSF_PMTHS) \n");
204            sqlText.append("SELECT DISTINCT ?, sd.emplid, p.iu_norm_work_months, p.iu_pay_months \n");
205            sqlText.append("FROM LD_BCN_BUILD_EXSALTOT02_MT sd, LD_BCN_POS_T p \n");
206            sqlText.append("WHERE sesid = ? AND sd.pos_csf_amt <> 0 AND sd.pos_csf_amt = \n");
207            sqlText.append(" (SELECT max(sd2.pos_csf_amt) FROM LD_BCN_BUILD_EXSALTOT02_MT sd2 \n");
208            sqlText.append("  WHERE sd2.sesid = sd.sesid AND sd2.emplid = sd.emplid) \n");
209            sqlText.append(" AND sd.position_nbr = (SELECT min(sd3.position_nbr) \n");
210            sqlText.append("  FROM LD_BCN_BUILD_EXSALTOT02_MT sd3 \n");
211            sqlText.append("  WHERE sd3.sesid = sd.sesid AND sd3.emplid = sd.emplid AND sd3.pos_csf_amt = sd.pos_csf_amt) \n");
212            sqlText.append("AND p.univ_fiscal_yr = ? AND p.position_nbr = sd.position_nbr \n");
213    
214            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
215            sqlText.delete(0, sqlText.length());
216            
217            /* merge the base and request appointment attributes and amount sums into a single table, and initialize the use_flag */
218            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT05_MT \n");
219            sqlText.append("(SESID, EMPLID, POS_CSF_AMT, RES_CSF_AMT, POS_CSF_TM_PCT, SAL_AMT,  \n");
220            sqlText.append(" SAL_PCT, SAL_FTE, SAL_MTHS, SAL_PMTHS, CSF_MTHS, CSF_PMTHS, USE_FLAG) \n");
221            sqlText.append("SELECT ?, sm.emplid, SUM(COALESCE(sd.pos_csf_amt,0)), 0, SUM(COALESCE(sd.pos_csf_tm_pct,0)), SUM(COALESCE(sd.sal_amt,0)), \n");
222            sqlText.append(" SUM(COALESCE(sd.sal_pct,0)), 0, sm.sal_mths, sm.sal_pmths, COALESCE(cm.csf_mths,0), COALESCE(cm.csf_pmths,0), 'Y' \n");
223            sqlText.append("FROM (LD_BCN_BUILD_EXSALTOT03_MT sm LEFT OUTER JOIN LD_BCN_BUILD_EXSALTOT04_MT cm \n");
224            sqlText.append(" ON ((sm.sesid = cm.sesid) AND (sm.emplid = cm.emplid))), LD_BCN_BUILD_EXSALTOT02_MT sd \n");
225            sqlText.append("WHERE sm.sesid = ? AND sd.sesid = sm.sesid AND sd.emplid = sm.emplid \n");
226            sqlText.append("GROUP BY sm.emplid, sm.sal_mths, sm.sal_pmths, cm.csf_mths, cm.csf_pmths \n");
227                
228            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
229            sqlText.delete(0, sqlText.length());
230    
231            /* restate the base (CSF) amount to account for changes in the request appointment attributes */
232            sqlText.append("UPDATE LD_BCN_BUILD_EXSALTOT05_MT \n");
233            sqlText.append("SET res_csf_amt = ROUND(COALESCE(((pos_csf_amt * sal_pct * sal_mths * csf_pmths) / (pos_csf_tm_pct * csf_mths * sal_pmths)), 0.00),0) \n");
234            sqlText.append("WHERE sesid = ? AND pos_csf_tm_pct <> 0 AND csf_mths <> 0 AND sal_pmths <> 0 \n");
235            
236            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
237            sqlText.delete(0, sqlText.length());
238            
239            /* restate the base (CSF) amount to account for changes in the request FTE */
240            sqlText.append("UPDATE LD_BCN_BUILD_EXSALTOT05_MT \n");
241            sqlText.append("SET res_csf_amt = ROUND(COALESCE(((res_csf_amt * sal_pmths) / csf_pmths), 0.00),0) \n");
242            sqlText.append("WHERE sesid = ? AND sal_pmths <> csf_pmths AND csf_pmths <> 0 \n");
243            
244            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
245            sqlText.delete(0, sqlText.length());
246                
247            /* calculate the fte for each person */
248            sqlText.append("UPDATE LD_BCN_BUILD_EXSALTOT05_MT \n");
249            sqlText.append("SET sal_fte = COALESCE((((sal_pct * sal_mths) / sal_pmths) / 100.0), 0.0) \n");
250            sqlText.append("WHERE sesid = ? AND sal_pmths <> 0 \n");
251                    
252            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
253            sqlText.delete(0, sqlText.length());
254            
255            /* for a run with a threshold, we need to set the use_flag to exclude rows with percent changes below the threshold */
256            sqlText.append("UPDATE LD_BCN_BUILD_EXSALTOT05_MT \n");
257            sqlText.append("SET USE_FLAG = 'N' \n");
258            sqlText.append("WHERE sesid = ? \n");
259            sqlText.append(" AND ROUND((((sal_amt - res_csf_amt) / res_csf_amt) * 100),1) < ? \n");
260            sqlText.append(" AND res_csf_amt <> 0 \n");
261            sqlText.append(" AND sal_amt <> 0 \n");
262            
263            reportReasonStatisticsWithThreshold.add(new SQLForStep(sqlText));
264            sqlText.delete(0, sqlText.length());   
265            
266            /* reset recs greater than percent change - keep lte */
267            sqlText.append("UPDATE LD_BCN_BUILD_EXSALTOT05_MT \n");
268            sqlText.append("SET USE_FLAG = 'N' \n");
269            sqlText.append("WHERE sesid = ? \n");
270            sqlText.append(" AND ROUND((((sal_amt - res_csf_amt) / res_csf_amt) * 100),1) > ? \n");
271            sqlText.append(" AND res_csf_amt <> 0 \n");
272            sqlText.append(" AND sal_amt <> 0 \n");
273            
274            reportReasonStatisticsWithThreshold.add(new SQLForStep(sqlText));
275            sqlText.delete(0, sqlText.length());   
276            
277            /* make a copy of the detailed rows by organization for continuing people */
278            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT06_MT \n");
279            sqlText.append("(SESID, ORG_FIN_COA_CD, ORG_CD, EMPLID, POS_CSF_AMT,  \n");
280            sqlText.append(" APPT_RQST_AMT, APPT_RQST_FTE_QTY, INIT_RQST_AMT, INIT_RQST_FTE) \n");
281            sqlText.append("SELECT DISTINCT ?, ctrl.sel_org_fin_coa, ctrl.sel_org_cd, ssni.emplid, ssni.res_csf_amt, \n");
282            sqlText.append(" ssni.sal_amt, ssni.sal_fte, 0, 0 \n");
283            sqlText.append("FROM LD_BCN_CTRL_LIST_T ctrl, LD_PNDBC_APPTFND_T bcaf, LD_BCN_OBJ_PICK_T pick, LD_BCN_BUILD_EXSALTOT05_MT ssni \n");
284            sqlText.append("WHERE ssni.res_csf_amt <> 0 \n");
285            sqlText.append(" AND ssni.use_flag = 'Y' \n");
286            sqlText.append(" AND ctrl.person_unvl_id = ? \n");
287            sqlText.append(" AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
288            sqlText.append(" AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
289            sqlText.append(" AND bcaf.account_nbr = ctrl.account_nbr \n");
290            sqlText.append(" AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
291            sqlText.append(" AND bcaf.emplid = ssni.emplid \n");
292            sqlText.append(" AND ssni.sesid = ? \n");
293            sqlText.append(" AND bcaf.fin_object_cd = pick.fin_object_cd \n");
294            sqlText.append(" AND pick.person_unvl_id = ctrl.person_unvl_id \n");
295            sqlText.append(" AND pick.select_flag > 0 \n");
296            
297            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
298            sqlText.delete(0, sqlText.length()); 
299            
300            /* create copy of detail rows by organization for new people (who do not get a raise and therefore satisfy any threshold tests) */
301            sqlText.append("INSERT INTO LD_BCN_BUILD_EXSALTOT06_MT \n");
302            sqlText.append("(SESID, ORG_FIN_COA_CD, ORG_CD, EMPLID, POS_CSF_AMT, \n");
303            sqlText.append("  APPT_RQST_AMT, APPT_RQST_FTE_QTY, INIT_RQST_AMT, INIT_RQST_FTE) \n");
304            sqlText.append("SELECT DISTINCT ?, ctrl.sel_org_fin_coa, ctrl.sel_org_cd, ssni.emplid, ssni.res_csf_amt, \n");
305            sqlText.append(" 0, 0, ssni.sal_amt, ssni.sal_fte \n");
306            sqlText.append("FROM LD_BCN_CTRL_LIST_T ctrl, LD_PNDBC_APPTFND_T bcaf, LD_BCN_OBJ_PICK_T pick, LD_BCN_BUILD_EXSALTOT05_MT ssni \n");
307            sqlText.append("WHERE ssni.res_csf_amt = 0 \n");
308            sqlText.append(" AND ssni.use_flag = 'Y' \n");
309            sqlText.append(" AND ctrl.person_unvl_id = ? \n");
310            sqlText.append(" AND bcaf.univ_fiscal_yr = ctrl.univ_fiscal_yr \n");
311            sqlText.append(" AND bcaf.fin_coa_cd = ctrl.fin_coa_cd \n");
312            sqlText.append(" AND bcaf.account_nbr = ctrl.account_nbr \n");
313            sqlText.append(" AND bcaf.sub_acct_nbr = ctrl.sub_acct_nbr \n");
314            sqlText.append(" AND bcaf.emplid = ssni.emplid \n");
315            sqlText.append(" AND ssni.sesid = ? \n");
316            sqlText.append(" AND bcaf.fin_object_cd = pick.fin_object_cd \n");
317            sqlText.append(" AND pick.person_unvl_id = ctrl.person_unvl_id \n");
318            sqlText.append(" AND pick.select_flag > 0 \n");
319              
320            reportReasonStatisticsWithNoThreshold.add(new SQLForStep(sqlText));
321            sqlText.delete(0, sqlText.length()); 
322              
323            /* sum all the detailed rows and insert into the report table */
324            sqlText.append("INSERT INTO LD_BCN_SLRY_TOT_T \n");
325            sqlText.append("(PERSON_UNVL_ID, ORG_FIN_COA_CD, ORG_CD, POS_CSF_AMT, \n");
326            sqlText.append("APPT_RQST_AMT, APPT_RQST_FTE_QTY, INIT_RQST_AMT, INIT_RQST_FTE_QTY) \n");
327            sqlText.append("SELECT ?, org_fin_coa_cd, org_cd, ROUND(SUM(pos_csf_amt),0),\n");
328            sqlText.append(" SUM(appt_rqst_amt), SUM(appt_rqst_fte_qty), SUM(init_rqst_amt), SUM(init_rqst_fte) \n");
329            sqlText.append("FROM  LD_BCN_BUILD_EXSALTOT06_MT \n");
330            sqlText.append("WHERE sesid = ? \n");
331            sqlText.append("GROUP BY org_fin_coa_cd, org_cd \n");
332            
333            updateReportsReasonStatisticsTable.add(new SQLForStep(sqlText));
334            sqlText.delete(0, sqlText.length()); 
335        }
336    
337        /**
338         * 
339         * @see org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionReasonStatisticsReportDao#cleanReportsReasonStatisticsTable(java.lang.String)
340         */
341        public void cleanReportsReasonStatisticsTable(String principalName) {
342            clearTempTableByUnvlId("LD_BCN_SLRY_TOT_T", "PERSON_UNVL_ID", principalName);
343        }
344        
345        /**
346         * 
347         * clears the rows for this session out of the work tables
348         * @param idForSession--a unique identifier for the session
349         */
350        public void cleanWorkTablesFromThisSession(String idForSession)
351        {
352            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT01_MT", "SESID", idForSession);
353            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT02_MT", "SESID", idForSession);
354            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT03_MT", "SESID", idForSession);
355            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT04_MT", "SESID", idForSession);
356            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT05_MT", "SESID", idForSession);
357            clearTempTableBySesId("LD_BCN_BUILD_EXSALTOT06_MT", "SESID", idForSession);
358            /**
359             * this is necessary to clear any rows for the tables we have just updated from the OJB cache.  otherwise, subsequent calls to OJB will fetch the old, unupdated cached rows.
360             */
361            persistenceService.clearCache();
362        }
363        
364        /**
365         * 
366         * works in both threshold and non-threshold mode to get the summary salary statistics and appointment attributes for each person
367         * @param principalName--the user running the report
368         * @param idForSession--a unique ID for the session of the user running the report
369         * @param previousFiscalYear--the fiscal year preceding the one for which we are preparing a budget
370         */
371        protected void adjustLastYearSalaryForAppointmentChanges(String principalName, String idForSession, Integer previousFiscalYear)
372        {
373            //  strings to be inserted into SQL
374            ArrayList<String >stringsToInsert = new ArrayList<String>(2);
375            stringsToInsert.add(BCConstants.VACANT_EMPLID);
376            stringsToInsert.add(BCConstants.AppointmentFundingDurationCodes.NONE.durationCode);
377            // get base (CSF) and request appointment attributes for people with no leave indicated 
378            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(0).getSQL(stringsToInsert), idForSession, principalName, idForSession);
379            // get base (CSF) and request appointment attributes for people who are marked as going on leave next year
380            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(1).getSQL(stringsToInsert), idForSession, principalName, idForSession);
381            // for each person, take the request appointment attributes from the record with the higest salary amount
382            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(2).getSQL(), idForSession, idForSession);
383            // for each continuing person, take the base (CSF) appointment attributes from the record with the highest base salary amount
384            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(3).getSQL(), idForSession, idForSession, previousFiscalYear);
385            // merge the appointment attributes and the sums of base and request salary and percent time into a single table
386            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(4).getSQL(), idForSession, idForSession);
387            // restate the base (CSF) salary to account for changes in last year's appointment attributes
388            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(5).getSQL(), idForSession);
389            // restate the base (CSF) salary to account for changes in attributes that affect FTE
390            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(6).getSQL(), idForSession);
391            // calculate an request FTE for each person
392            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(7).getSQL(), idForSession);
393        }
394        
395        /**
396         * 
397         * get detailed salary/FTE rows by person and organization for the continuing people to be reported 
398         * @param principalName
399         * @param idForSession
400         */
401        protected void fetchIndividualDetailForContinuingPeople(String principalName, String idForSession)
402        {
403            // salaries and FTE by EMPLID and organization for people in the payroll in the base budget year
404            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(8).getSQL(), idForSession, principalName, idForSession);
405        }
406        
407        
408        /**
409         * 
410         * @see org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionReasonStatisticsReportDao#reportReasonStatisticsWithAThreshold(java.lang.String, java.lang.Integer, boolean, org.kuali.rice.kns.util.KualiDecimal)
411         */
412        public void updateReasonStatisticsReportsWithAThreshold(String principalName, Integer previousFiscalYear, boolean reportIncreasesAtOrAboveTheThreshold, KualiDecimal thresholdPercent) {
413    
414            // get a unique session ID   
415            String idForSession = (new Guid()).toString();
416            cleanReportsReasonStatisticsTable(principalName);
417            // build the list of constant strings to insert into the SQL
418            ArrayList<String> stringsToInsert = new ArrayList<String>(1);
419            stringsToInsert.add(BCConstants.VACANT_EMPLID);
420    
421            // for a report by threshold, we want everyone--exclude only vacant lines
422            getSimpleJdbcTemplate().update(reportReasonStatisticsWithThreshold.get(0).getSQL(stringsToInsert), idForSession, principalName);
423            // get all the salary and appointment information for those people
424            adjustLastYearSalaryForAppointmentChanges(principalName, idForSession, previousFiscalYear);
425            
426            // mark the rows to be excluded when we are screening with a threshold percent
427            // (KualiDecimal is not recognized as a type by java.sql--we have to convert it to its superclass BigDecimal)
428            BigDecimal thresholdValue = thresholdPercent.bigDecimalValue();
429            if (reportIncreasesAtOrAboveTheThreshold)
430            {
431                // exclude everyone with increases less than the threshold
432                getSimpleJdbcTemplate().update(reportReasonStatisticsWithThreshold.get(1).getSQL(), idForSession, thresholdValue);
433            }
434            else
435            {
436                // exclude everyone with increases over the threshold
437                getSimpleJdbcTemplate().update(reportReasonStatisticsWithThreshold.get(2).getSQL(), idForSession, thresholdValue);
438            }
439            
440            fetchIndividualDetailForContinuingPeople(principalName, idForSession);
441            sumTheDetailRowsToProduceTheReportData (principalName, idForSession);
442            
443            cleanWorkTablesFromThisSession(idForSession);
444            /**
445             * this is necessary to clear any rows for the tables we have just updated from the OJB cache.  otherwise, subsequent calls to OJB will fetch the old, unupdated cached rows.
446             */
447            persistenceService.clearCache();
448        }
449    
450        /**
451         * 
452         * @see org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionReasonStatisticsReportDao#reportReasonStatisticsWithoutAThreshold(java.lang.String, java.lang.Integer)
453         */
454        public void updateReasonStatisticsReportsWithoutAThreshold(String principalName, Integer previousFiscalYear) {
455            // get a unique session ID   
456            String idForSession = (new Guid()).toString();
457            cleanReportsReasonStatisticsTable(principalName);
458            
459            // build the list of constant strings to insert into the SQL
460            ArrayList<String> stringsToInsert = new ArrayList<String>(1);
461            stringsToInsert.add(BCConstants.VACANT_EMPLID);
462    
463            // we want only people who have an attached reason code
464            getSimpleJdbcTemplate().update(reportReasonStatisticsWithNoThreshold.get(0).getSQL(stringsToInsert), idForSession, principalName);
465            // get all the salary and appointment information for those people
466            adjustLastYearSalaryForAppointmentChanges(principalName, idForSession, previousFiscalYear);
467            
468            fetchIndividualDetailForContinuingPeople(principalName, idForSession);
469            // when we are using a reason code and not a threshold, we want everyone with a reason code, not just continuing people
470            // new people have no percent increase, and so would not match any threshold, but should be included under this report option
471            getSimpleJdbcTemplate().update(reportReasonStatisticsWithNoThreshold.get(1).getSQL(), idForSession, principalName, idForSession);
472            sumTheDetailRowsToProduceTheReportData (principalName, idForSession);
473            
474            cleanWorkTablesFromThisSession(idForSession);
475            /**
476             * this is necessary to clear any rows for the tables we have just updated from the OJB cache.  otherwise, subsequent calls to OJB will fetch the old, unupdated cached rows.
477             */
478            persistenceService.clearCache();
479        }
480        
481        /**
482         * 
483         * sum base and request amounts and FTE by organization to produce the data used by the report
484         * @param idForSession--the session of the user doing the report
485         */
486        protected void sumTheDetailRowsToProduceTheReportData (String principalName, String idForSession) {
487            getSimpleJdbcTemplate().update(updateReportsReasonStatisticsTable.get(9).getSQL(), principalName, idForSession);
488        }
489        
490        public void setPersistenceService(PersistenceService persistenceService)
491        {
492            this.persistenceService = persistenceService;
493        }
494    
495    }
496