blob: ef874519d5127cb6319116c6960198576f918e22 [file] [log] [blame]
/*
* @author max
*/
package com.intellij.psi;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.impl.DebugUtil;
import com.intellij.testFramework.LightCodeInsightTestCase;
import org.jetbrains.annotations.NonNls;
public class TreeIsCorrectAfterDiffReparseTest extends LightCodeInsightTestCase {
public void testIDEADEV41862() throws Exception {
@NonNls String part1 = "package com.test;\n" +
"\n" +
"\n" +
"//------------------------------------------------------------------\n" +
"// Copyright (c) 1999, 2007\n" +
"// WorkForce Software, Inc.\n" +
"// All rights reserved.\n" +
"//\n" +
"// Web-site: http://www.workforcesoftware.com\n" +
"// E-mail: support@workforcesoftware.com\n" +
"// Phone: (877) 493-6723\n" +
"//\n" +
"// This program is protected by copyright laws and is considered\n" +
"// a trade secret of WorkForce Software. Access to this program\n" +
"// and source code is granted only to licensed customers. Under\n" +
"// no circumstances may this software or source code be distributed\n" +
"// without the prior written consent of WorkForce Software.\n" +
"// -----------------------------------------------------------------\n" +
"\n" +
"import com.workforcesoftware.Data.Employee.*;\n" +
"import com.workforcesoftware.Data.*;\n" +
"import com.workforcesoftware.Data.assignment.Asgnmt_master;\n" +
"import com.workforcesoftware.Data.assignment.Asgnmt;\n" +
"import com.workforcesoftware.Data.Output.Time_sheet_output;\n" +
"import com.workforcesoftware.Data.TimeSched.PayPeriodData;\n" +
"import com.workforcesoftware.Data.TimeSched.TimeSchedUtils;\n" +
"import com.workforcesoftware.Data.TimeSched.Schedule.Schedule_detailList;\n" +
"import com.workforcesoftware.Data.TimeSched.TimeSheet.*;\n" +
"import com.workforcesoftware.Data.assignment.Asgnmt_masterList;\n" +
"import com.workforcesoftware.Exceptions.*;\n" +
"import com.workforcesoftware.Gen.Choice.Approval_event_type;\n" +
"import com.workforcesoftware.Gen.Choice.Transaction_status;\n" +
"import com.workforcesoftware.Gen.Choice.Messages;\n" +
"import com.workforcesoftware.Gen.Choice.Program_source;\n" +
"import com.workforcesoftware.Gen.Other.DbRec.DbRecTime_sheet_output;\n" +
"import com.workforcesoftware.Gen.Other.DbRec.DbRecTime_sheet;\n" +
"import com.workforcesoftware.Gen.Other.List.WDateList;\n" +
"import com.workforcesoftware.Gen.Policy.Right_grp;\n" +
"import com.workforcesoftware.Gen.Policy.Policy_profile;\n" +
"import com.workforcesoftware.Policy.*;\n" +
"import com.workforcesoftware.Util.DateTime.WDate;\n" +
"import com.workforcesoftware.Util.DateTime.WDateTime;\n" +
"import com.workforcesoftware.Util.DB.ListWriter;\n" +
"import com.workforcesoftware.Util.DB.DbRecFieldCopier;\n" +
"import com.workforcesoftware.ClientRequests.TimeSched.TimeSchedUtil;\n" +
"import com.workforcesoftware.ClientRequests.TimeSched.PayPeriodInfo;\n" +
"import com.workforcesoftware.ClientRequests.TimeEntry.ApprovalInfo;\n" +
"import com.workforcesoftware.ClientRequests.TimeEntry.BankBalanceResults;\n" +
"import com.workforcesoftware.Misc.ServerErrorLogger;\n" +
"import com.workforcesoftware.Misc.ServerError;\n" +
"import com.workforcesoftware.AssignmentPeriod.*;\n" +
"import com.workforcesoftware.AssignmentPeriod.TimeSheetState;\n" +
"import com.workforcesoftware.Dictionary.DataDictionary;\n" +
"\n" +
"import java.sql.SQLException;\n" +
"import java.util.*;\n" +
"import org.log4j.Category;\n" +
"\n" +
"/**\n" +
" * Holds definition of {@link CalcInfo}\n" +
" */\n" +
"class AssignmentManager {\n" +
" /**\n" +
" * Implementation of {@link TimeSheetCalculationInfo}.\n" +
" * Extracts all the required data from AllCalcDataManager, AllCalculationData, Time_sheet, etc.\n" +
" * Makes copies of all mutable data, unmodifiable lists where possible.\n" +
" */\n" +
" static class CalcInfo implements TimeSheetCalculationInfo {\n" +
" /**\n" +
" * Use the passed in AllCalculationData object - DO NOT try to get it from the passed in AllCalcDataManager because\n" +
" * the ACD object that's passed in is obtained from AssignmentManager.getAllCalcData method which handles the\n" +
" * scenario where a prior period may need to be amended automatically and if ACDM.getAllCalcData() method is called,\n" +
" * you might get an unmodifiable ACD\n" +
" * @param acdm\n" +
" * @param acd\n" +
" * @param timeSheetId\n" +
" * @param parms\n" +
" */\n" +
" CalcInfo(AllCalcDataManager acdm, AllCalculationData acd, TimeSheetIdentifier timeSheetId, TimeEntryParms parms) {\n" +
" // we'll extract all these vars separately, for readability\n" +
" final Right_grp rightGrp = parms.getRight_grp(timeSheetId);\n" +
" final AllPolicies ap = acd.getAllPolicies();\n" +
" final WDate ppEndForToday = TimeSchedUtil.getPayPeriodRangeForToday(acdm).getEnd();\n" +
"\n" +
" this.acd = acd;\n" +
" this.timeSheetIdentifier = timeSheetId;\n" +
" this.approval_eventList = acd.getApproval_eventList().getUnmodifiableList();\n" +
" this.policyProfile = acd.getAsgnmtMaster().getPolicyProfilePolicy(ppEndForToday, ap);\n" +
" this.adjustmentsPaidWithTimesheet = createAdjustmentsPaidWithTimesheet(acdm, acd);\n" +
" this.bankBalanceResults = acdm.getBankDataForBankBalancePreview(ap, acd, rightGrp.getRight_grp(), acd.getPP_end());\n" +
" this.tsoListForPayPreview = Collections.unmodifiableList(acdm.getTsoListForPayPreview(acd, rightGrp.getRight_grp()));\n" +
" this.approvedDays = createApprovedDays(acd);\n" +
"\n" +
" try {\n" +
" // these are fairly involved calculations, and we want to validate the pay period list,\n" +
" // so we'll do them both here together instead of upon request\n" +
" employeePeriodInfo = acdm.getEmployeePeriodInfo(timeSheetId.getPpEnd());\n" +
" payPeriodList = TimeSchedUtil.calcPayPeriodList(acdm, rightGrp.getRight_grp());\n" +
" }\n" +
" catch (Exception e) {\n" +
" throw new InternalApplicationException(\"Loading calc data for: \" + timeSheetId, e);\n" +
" }\n" +
" assertPayPeriodList();\n" +
"\n" +
" AssignmentPeriodStateImpl aps = new AssignmentPeriodStateImpl(employeePeriodInfo,\n" +
" rightGrp, parms.getSystemFeature(), ap, ppEndForToday, acdm, policyProfile);\n" +
" assignmentPeriodState = aps;\n" +
" timeSheetState = new TimeSheetStateImpl(this, aps, policyProfile);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns an ApprovalInfo object, which contains information about whether\n" +
" * employee/manager approved a timesheet.\n" +
" *\n" +
" * @see ApprovalInfo\n" +
" */\n" +
" public ApprovalInfo getApprovalInfo() { return new ApprovalInfo(acd.getTime_sheet(), approval_eventList); }\n" +
"\n" +
" public Collection<DbRecTime_sheet_output> getAdjustmentsPaidWithTimesheet() {\n" +
" return adjustmentsPaidWithTimesheet;\n" +
" }\n" +
"\n" +
" public Approval_eventList getApproval_eventList() {\n" +
" return approval_eventList;\n" +
" }\n" +
"\n" +
"\n" +
" /**\n" +
" * Returns state information about a given assignment and period, encompassing\n" +
" * the cross-section of security related settings calculated from:\n" +
" * <ul>\n" +
" * <li>App_user Roles (app_user_right and right_grp tables)\n" +
" * <li>Assignment effective dates\n" +
" * <li>Employee record effective dates\n" +
" * <li>System_feature read/write privileges\n" +
" * </ul>\n" +
" *\n" +
" * @return not ever null\n" +
" */\n" +
" public AssignmentPeriodState getAssignmentPeriodState() {\n" +
" return assignmentPeriodState;\n" +
" }\n" +
"\n" +
" public Set<WDate> getDailyApprovalDays() {\n" +
" return approvedDays;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns map to the accrual banks data, which is a read only reference\n" +
" * of the information contained there. The banks returned will be all banks attached\n" +
" * to the assignment, including aggregate banks if applicable.\n" +
" *\n" +
" * The list will not contain any banks that the current App_user's Right_grp cannot view for this assignment's\n" +
" * Policy_profile (as defined by {@link com.workforcesoftware.Gen.Policy.User_entry_rule_detail#getDisplay_bank_set()})\n" +
" */\n" +
" public List<BankBalanceResults> getBankBalanceResults() {\n" +
" return bankBalanceResults;\n" +
" }\n" +
"\n" +
" public Approval_event getLastValidEmployeeApproveEvent() {\n" +
" return approval_eventList.getLastValidEmployeeApproveEvent();\n" +
" }\n" +
"\n" +
" public Time_sheet_detail_splitList getTime_sheet_detail_splitList() {\n" +
" return acd.getTime_sheet().getTime_sheet_detail_splitList().getUnmodifiableList();\n" +
" }\n" +
"\n" +
" public Time_sheet_detailList getTime_sheet_detailList() {\n" +
" return acd.getTime_sheet().getDetailList().getUnmodifiableList();\n" +
" }\n" +
"\n" +
" public Schedule_detailList getSchedule_detailList() {\n" +
" return acd.getSchedule().getDetailList().getUnmodifiableList();\n" +
" }\n" +
"\n" +
" public Time_sheet_exceptionList getTime_sheet_exceptionList() {\n" +
" return acd.getTime_sheet().getTime_sheet_exceptionList().getUnmodifiableList();\n" +
" }\n" +
"\n" +
" public List<Time_sheet_output> getTsoListForPayPreview() {\n" +
" return tsoListForPayPreview;\n" +
" }\n" +
"\n" +
" public Approval_eventList getValidManagerApproveEvents() {\n" +
" return approval_eventList.getValidManagerApproveEvents();\n" +
" }\n" +
"\n" +
" public TimeSheetIdentifier getTimeSheetIdentifier() {\n" +
" return timeSheetIdentifier;\n" +
" }\n" +
"\n" +
" public EmployeePeriodInfo getEmployeePeriodInfo() {\n" +
" return employeePeriodInfo;\n" +
" }\n" +
"\n" +
" public Employee_master getEmployeeMaster() {\n" +
" return new Employee_master(acd.getEmployee_master());\n" +
" }\n" +
"\n" +
" public Employee getEmployee() {\n" +
" return new Employee(acd.getEmployee_master().getEmployeeAsOfOrAfter(getPPEnd()));\n" +
" }\n" +
"\n" +
" public Asgnmt_master getAsgnmtMaster() {\n" +
" return new Asgnmt_master(acd.getAsgnmtMaster());\n" +
" }\n" +
"\n" +
" public Asgnmt getAsgnmt() {\n" +
" return new Asgnmt(acd.getAsgnmt());\n" +
" }\n" +
"\n" +
" public WDate getPPEnd() {\n" +
" return employeePeriodInfo.getPp_end();\n" +
" }\n" +
"\n" +
" public WDate getPPBegin() {\n" +
" return employeePeriodInfo.getPp_begin();\n" +
" }\n" +
"\n" +
" public Policy_profile getPolicy_profile() {\n" +
" return policyProfile;\n" +
" }\n" +
"\n" +
"\n" +
" /**\n" +
" * Return state information about the timesheet, which considers security\n" +
" * relationship between the current state (locked/closed/amended) in relation to:\n" +
" * <ul>\n" +
" * <li>App_user Roles (app_user_right and right_grp tables)\n" +
" * <li>Assignment effective dates\n" +
" * <li>Employee record effective dates\n" +
" * <li>System_feature read/write privileges\n" +
" * <li>Approval level of the timesheet\n" +
" * </ul>\n" +
" *\n" +
" * @return not ever null\n" +
" */\n" +
" public TimeSheetState getTimeSheetState() {\n" +
" return timeSheetState;\n" +
" }\n" +
"\n" +
" public int getVersionNumber() {\n" +
" return timeSheetIdentifier.getVersion();\n" +
" }\n" +
"\n" +
" public PayPeriodData getPayPeriodData() {\n" +
" return acd.getPayPeriodData();\n" +
" }\n" +
"\n" +
" public boolean getIsNewlyUserAmended() {\n" +
" return acd.isNewlyUserAmended();\n" +
" }\n" +
"\n" +
" public List<PayPeriodInfo> getPayPeriodList() {\n" +
" return payPeriodList;\n" +
" }\n" +
"\n" +
" private static Collection<DbRecTime_sheet_output> createAdjustmentsPaidWithTimesheet(AllCalcDataManager acdm, AllCalculationData acd) {\n" +
" try { return Collections.unmodifiableCollection(acdm.getAdjustmentsPaidWithTimesheet(acd)); }\n" +
" catch (SQLException e) { throw new InternalApplicationException(\"Unexpected SQL Exception\", e); }\n" +
" }\n" +
"\n" +
" private void assertPayPeriodList() {\n" +
" //TODO - probably need to show a \"nice\" message to the user saying that the employee doesn't\n" +
" //TODO - have a viewable/editable pay period.\n" +
" if(payPeriodList.isEmpty()) {\n" +
" throw new InternalApplicationException(\"No viewable/editable pay periods found for employee \"\n" +
" + employeePeriodInfo.getEmployeeMaster().getEmployee());\n" +
" }\n" +
" }\n" +
"\n" +
" private static Set<WDate> createApprovedDays(AllCalculationData acd) {\n" +
" // calculate days approved by daily approvals\n" +
" Set<WDate> approvedDays = new HashSet<WDate>();\n" +
" for( WDate date : acd.getPayPeriodData().getActiveDates().getAllDates() ) {\n" +
" if( acd.isDayApproved(date) ) { approvedDays.add(date); }\n" +
" }\n" +
" return Collections.unmodifiableSet(approvedDays);\n" +
" }\n" +
"\n" +
" private final Collection<DbRecTime_sheet_output> adjustmentsPaidWithTimesheet;\n" +
" private final Approval_eventList approval_eventList;\n" +
" private final AssignmentPeriodState assignmentPeriodState;\n" +
" private final List<BankBalanceResults> bankBalanceResults;\n" +
" private final List<Time_sheet_output> tsoListForPayPreview;\n" +
" private final TimeSheetIdentifier timeSheetIdentifier;\n" +
" private final TimeSheetState timeSheetState;\n" +
" private final EmployeePeriodInfo employeePeriodInfo;\n" +
" private final ArrayList<PayPeriodInfo> payPeriodList;\n" +
" private final Set<WDate> approvedDays;\n" +
" private final Policy_profile policyProfile;\n" +
" private final AllCalculationData acd;\n" +
" }\n" +
"\n" +
" AssignmentManager(GeneratedId assignmentId, Set<TimeSheetIdentifier> timeSheetIdentifiers, TimeEntryParmsPerAssignment parms) {\n" +
" this.asgnmtMaster = getAggregateOrSingleAssignmentMaster(assignmentId);\n" +
" setClassFields(parms, timeSheetIdentifiers);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Obtains the single or aggregate assignment master for the given assignmentId. If the given assignmentId is not a\n" +
" * single or aggregate assignment, fetches the aggregate associated with the given assignmentId and returns that.\n" +
" *\n" +
" * @param assignmentId Id of the assigment to load.\n" +
" * @return the single or aggregate assignment master for the given assignmentId.\n" +
" */\n" +
" private static Asgnmt_master getAggregateOrSingleAssignmentMaster(GeneratedId assignmentId) {\n" +
" Asgnmt_master am = Asgnmt_master.load(assignmentId);\n" +
" if(am.isAggregate() || am.isSingle()) {\n" +
" return am;\n" +
" }\n" +
"\n" +
" //If it's not a single or aggregate, we need to load the aggregate and load that.\n" +
" return Asgnmt_master.load(am.getAggregate_asgnmt());\n" +
" }\n" +
"\n" +
" AggregateTimeEntryTransactionResults load() {\n" +
" return loadImpl();\n" +
" }\n" +
"\n" +
" /**\n" +
" * Reverts any changes that have not been committed to the database on this AssignmentManager\n" +
" */\n" +
" void revert() {\n" +
" updatedAllCalcDataManager = null;\n" +
" }\n" +
"\n" +
" /**\n" +
" * @return Return initial transaction results from originalAllCalcData, which must be set before calling this.\n" +
" */\n" +
" private AggregateTimeEntryTransactionResults loadImpl() {\n" +
" AggregateTimeEntryTransactionResults aggregateResults = (AggregateTimeEntryTransactionResults) parms.getTimeEntryResultsFactory().newInstance();\n" +
" for (TimeSheetIdentifier timeSheetId : getTimeSheetIdentifiers()) {\n" +
" AllCalculationData acd = getOriginalAllCalcData(timeSheetId);\n" +
" // Make sure system_record_id's are assigned - TimeEntryManager and client do not function correctly without\n" +
" assignSystemRecordIds(acd);\n" +
" CalcInfo calcInfo = new CalcInfo(getOriginalAllCalcDataManager(timeSheetId.getAsgnmt()), acd, timeSheetId, parms);\n" +
" //todo-ute-nazim do we really need to perform TimeSheetDiff for the initial load ?\n" +
" TimeEntryTransactionResults singleTimeSheetResults =\n" +
" new TimeSheetDiff(timeSheetId, acd, calcInfo, TimeSheetCounters.getEmptyCounters(), NewToOldIdMap.EMPTY,\n" +
" Collections.EMPTY_LIST, new StatusMapsForTimesheetAndSchedule(), true, parms, null);\n" +
" aggregateResults.add(singleTimeSheetResults);\n" +
" }\n" +
" return aggregateResults;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Assign system_record_id's for time_sheet, time_sheet_detail's and time_sheet_exception's\n" +
" * @param acd\n" +
" */\n" +
" private void assignSystemRecordIds(AllCalculationData acd) {\n" +
" assignSystemRecordId(acd.getTime_sheet());\n" +
" assignSystemRecordIds(acd.getTime_sheet().getTime_sheet_detailList());\n" +
" assignSystemRecordIds(acd.getTime_sheet().getTime_sheet_exceptionList());\n" +
" }\n" +
"\n" +
" /**\n" +
" * todo: store the event.getComment()\n" +
" * @param event\n" +
" */\n" +
" AggregateTimeEntryTransactionResults amend(TransactionApprovalEvent event) {\n" +
" TimeSheetIdentifier singleTimeSheetId = event.getTimeSheetIdentifier();\n" +
" //TODO: Shouldn't this call createOriginalAllCalcDataManagerIfNeeded? Seems like we needlessly reload the original\n" +
" //TODO: ACDM. This is because a timesheet which was prepared for amendment couldn't have been modified before it\n" +
" //TODO: was amended.\n" +
" invalidateAllCalcDataManagers();\n" +
"\n" +
" // Create the amended ACD and put into the ACDM cache\n" +
" EmployeePeriodInfo epInfo = null;\n" +
" try {\n" +
" AllCalcDataManager acdm = getOriginalAllCalcDataManager(singleTimeSheetId.getAsgnmt());\n" +
" epInfo = acdm.getEmployeePeriodInfo(singleTimeSheetId.getPpEnd());\n" +
" AllCalculationData amendedAllCalcData = acdm.createAmendedAllCalcData(epInfo, parms.getApp_user(), WDateTime.now());\n" +
" // No need to hook this up to approval change logic, since it is an in-memory change so far.\n" +
" //Recalculate the amended ACD object so that timesheet exceptions that are on the closed version (if any)\n" +
" // get created on the amended timesheet as well\n" +
" amendedAllCalcData.recalc(acdm);\n" +
" }\n" +
" catch (SQLException e) {\n" +
" throw new InternalApplicationException(\"amending time sheet for \" + singleTimeSheetId, e);\n" +
" }\n" +
" catch (MultipleRowDbRecException e) {\n" +
" throw new InternalApplicationException(\"amending time sheet for \" + singleTimeSheetId, e);\n" +
" } catch (Exception e) {\n" +
" throw new InternalApplicationException(\"amending time sheet for \" + singleTimeSheetId, e);\n" +
" }\n" +
" return loadImpl();\n" +
" }\n" +
"\n" +
" TimeSheetTransactionApplier applyTransaction(TimeSheetIdentifier id, TimeEntryTransaction trans) {\n" +
" AllCalcDataManager updatedAcdm = getUpdatedAllCalcDataManager(id.getAsgnmt());\n" +
" AllCalculationData acd = getAllCalcData(updatedAcdm, id);\n" +
" return new TimeSheetTransactionApplier(trans, updatedAcdm, acd, parms, id);\n" +
" }\n" +
"\n" +
" private void recalc(TimeSheetIdentifier timeSheetId, Approval_event_type approvalEventType) {\n" +
" AllCalcDataManager updatedAcdm = getUpdatedAllCalcDataManager(timeSheetId.getAsgnmt());\n" +
" AllCalculationData acd = getAllCalcData(updatedAcdm, timeSheetId);\n" +
" try {\n" +
" if (approvalEventType == Approval_event_type.SAVE_SCHEDULE) {\n" +
" // if we're saving a change to the schedule, the timesheet might need to be re-initialized.\n" +
" acd.reinitTimeSheet(updatedAcdm, true);\n" +
" }\n" +
" acd.recalc(updatedAcdm);\n" +
" }\n" +
" catch (Exception e) {\n" +
" throw new InternalApplicationException(\"calculate for \" + timeSheetId, e);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Save one timesheet to a ListWriter using AllCalculationData.\n" +
" * @param timeSheetId\n" +
" * @param saveEvent\n" +
" * @throws Exception\n" +
" */\n" +
" void save(TimeSheetIdentifier timeSheetId, Approval_event_type saveEvent) throws Exception {\n" +
" GeneratedId asgnmtId = timeSheetId.getAsgnmt();\n" +
" AllCalcDataManager acdMgr = getUpdatedAllCalcDataManager(asgnmtId);\n" +
" AllCalculationData acd = getAllCalcData(acdMgr, timeSheetId);\n" +
"\n" +
" // TODO: it'd be nice if we were able to retry a save if it failed because of unrelated changes (to accomodate for\n" +
" // concurrent save operations on other component timesheets besides the one that the user is trying to save)\n" +
" final ListWriter lw = new ListWriter();\n" +
" acd.saveAfterApplyingApprovalEvent(acdMgr, saveEvent, lw);\n" +
"\n" +
" //The exceptions must be generated after the ACD has performed approval related operations.\n" +
" // Loading the original ACD to get the timesheet object before the approval. This will be used to determine\n" +
" // timesheet's approval level before this save event.\n" +
" AllCalculationData originalAcd = getAllCalcData(getOriginalAllCalcDataManager(asgnmtId), timeSheetId);\n" +
" acdMgr.generateExceptionNotifications(acd, originalAcd.getTime_sheet(), acd.getTime_sheet(), lw);\n" +
" lw.writeLists();\n" +
"\n" +
" }\n" +
"\n" +
" /**\n" +
" * If time sheets were saved and written to the database successfully, update internal data\n" +
" * to reflect what's in the database as the new \"original\" data.\n" +
" */\n" +
" void postSave() {\n" +
" originalAllCalcDataManager = updatedAllCalcDataManager;\n" +
" updatedAllCalcDataManager = null;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns true if the given timesheet contains exceptions that should prevent save.\n" +
" * @param timeSheetId time sheet ID\n" +
" * @return true if exceptions exist that should prevent save, false otherwise.\n" +
" */\n" +
" boolean hasExceptionsPreventingSave(TimeSheetIdentifier timeSheetId) {\n" +
" AllCalculationData acd = getUpdatedAllCalcData(timeSheetId);\n" +
" try {\n" +
" return acd.getTime_sheet().getTime_sheet_exceptionList().getDisallow_timesheet_save(acd.getAllPolicies());\n" +
" }\n" +
" catch (PolicyLookupRequiredException e) {\n" +
" throw new InternalApplicationException(\"Policy error on timesheet \" + timeSheetId);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns true if the given timesheet contains exceptions that should prevent submit.\n" +
" * @param timeSheetId time sheet ID\n" +
" * @return true if exceptions exist that should prevent submit, false otherwise.\n" +
" */\n" +
" boolean hasExceptionsPreventingSubmit(TimeSheetIdentifier timeSheetId) {\n" +
" AllCalculationData acd = getUpdatedAllCalcData(timeSheetId);\n" +
" try {\n" +
" return acd.getTime_sheet().getTime_sheet_exceptionList().getDisallow_timesheet_submit(acd.getAllPolicies());\n" +
" }\n" +
" catch (PolicyLookupRequiredException e) {\n" +
" throw new InternalApplicationException(\"Policy error on timesheet \" + timeSheetId);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Compute {@link TimeEntryTransactionResults} changes to be returned in client.\n" +
" * @param timeSheetId\n" +
" * @param trans transaction which client just sent\n" +
" * @param applyResults results (errors) of applying the transaction\n" +
" * @param timeSheetSavedStatus a status to be added representing the save results - SAVED or ERROR, or null if none.\n" +
" * @param transactionSuccessful value for {@link TimeEntryTransactionResults#getTransactionWasSuccessful}.\n" +
" */\n" +
" TimeSheetDiff getDiff(TimeSheetIdentifier timeSheetId, TimeEntryTransaction trans,\n" +
" TimeSheetTransactionApplier applyResults, TransactionStatus timeSheetSavedStatus,\n" +
" boolean transactionSuccessful)\n" +
" {\n" +
" AllCalcDataManager acdm = getLatestAllCalcDataManager(timeSheetId.getAsgnmt());\n" +
" AllCalculationData acd = getAllCalcData(acdm, timeSheetId);\n" +
" NewToOldIdMap newToOldIdMap = applyResults.getNewToOldIdMap();\n" +
" StatusMapsForTimesheetAndSchedule timeSheetRowStatusMap = applyResults.getTimeEntryRowErrorMap();\n" +
"\n" +
" if (cat.isDebugEnabled()) {\n" +
" ObjectDumper.debug(cat, \"TimeSheetDiff getDiff before\", timeSheetRowStatusMap, null, true);\n" +
" }\n" +
"\n" +
" List<TransactionStatus> timeSheetStatuses = new ArrayList<TransactionStatus>(applyResults.getTimeSheetErrors());\n" +
" TimeSheetCounters counters = new TimeSheetCounters(trans, timeSheetId, newToOldIdMap);\n" +
" if (timeSheetSavedStatus != null) {\n" +
" timeSheetStatuses.add(timeSheetSavedStatus);\n" +
" if (timeSheetSavedStatus.getTransaction_status() == Transaction_status.SAVED ||\n" +
" timeSheetSavedStatus.getTransaction_status() == Transaction_status.ERROR) {\n" +
" // if we saved or there is an error mark every TimeEntryRow in the transaction as such.\n" +
" // get all row id's from the transaction (new id's - not temp id's)\n" +
" Collection transTimeSheetRowIds = applyResults.getTransactionTimeSheetRowIds();\n" +
" Collection transScheduleRowIds = applyResults.getTransactionScheduleRowIds();\n" +
" timeSheetRowStatusMap = new StatusMapsForTimesheetAndSchedule(transTimeSheetRowIds, transScheduleRowIds, timeSheetSavedStatus);\n" +
" timeSheetRowStatusMap.addAll(applyResults.getTimeEntryRowErrorMap());\n" +
" }\n" +
" }\n" +
"\n" +
" if (cat.isDebugEnabled()) {\n" +
" ObjectDumper.debug(cat, \"TimeSheetDiff getDiff after\", timeSheetRowStatusMap, null, true);\n" +
" }\n" +
"\n" +
" CalcInfo calcInfo = new CalcInfo(acdm, acd, timeSheetId, parms);\n" +
" return new TimeSheetDiff(timeSheetId, acd, calcInfo, counters, newToOldIdMap, timeSheetStatuses,\n" +
" timeSheetRowStatusMap, transactionSuccessful, parms, trans);\n" +
" }\n" +
"\n" +
" TimeSheetCalculationInfo getCalculationInfo(TimeSheetIdentifier timeSheetIdentifier) {\n" +
" validate(timeSheetIdentifier);\n" +
" final AllCalcDataManager acdm = getLatestAllCalcDataManager(timeSheetIdentifier.getAsgnmt());\n" +
" return new CalcInfo(acdm, getAllCalcData(acdm, timeSheetIdentifier), timeSheetIdentifier, parms);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Return the calculation info without auto-amending\n" +
" * @param acdm\n" +
" * @param timeSheetIdentifier\n" +
" * @return\n" +
" */\n" +
" private TimeSheetCalculationInfo getCalculationInfo(AllCalcDataManager acdm, TimeSheetIdentifier timeSheetIdentifier) {\n" +
" validate(timeSheetIdentifier);\n" +
" try {\n" +
" return new CalcInfo(acdm, acdm.getAllCalcData(timeSheetIdentifier.getPpEnd(), timeSheetIdentifier.getVersion()), timeSheetIdentifier, parms);\n" +
" } catch (Exception e) {\n" +
" throw new InternalApplicationException(\"Error creating CalcInfo \", e); \n" +
" }\n" +
" }\n" +
"\n" +
" private void invalidateAllCalcDataManagers() {\n" +
" originalAllCalcDataManager = null;\n" +
" updatedAllCalcDataManager = null;\n" +
" }\n" +
"\n" +
" private void createUpdatedAllCalcDataManager(Set<TimeSheetIdentifier> timeSheetIds) {\n" +
" AllCalcDataManager origAcdMgr = getOriginalAllCalcDataManager(asgnmtMaster.getAsgnmt());\n" +
" try {\n" +
" WDateList dateList = new WDateList();\n" +
" for(TimeSheetIdentifier id : timeSheetIds) {\n" +
" dateList.add(id.getPpEnd());\n" +
" }\n" +
" updatedAllCalcDataManager = origAcdMgr.copyForUpdate(dateList);\n" +
" } catch (Exception e) {\n" +
" throw new InternalApplicationException(\"Cloning for \" + timeSheetIds, e);\n" +
" }\n" +
" }\n" +
"\n" +
" private void validate(TimeSheetIdentifier timeSheetId) throws IllegalArgumentException {\n" +
" if (!timeSheetIdentifiers.contains(timeSheetId)) {\n" +
" throw new IllegalArgumentException(\"Invalid time sheet specified: \" + timeSheetId\n" +
" + \". not in \" + timeSheetIdentifiers);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Obtains the original AllCalcDataManager for the given assignment Id. The assignment id must be given because in a\n" +
" * multiple assignment environment, a specific component needs to be requested, as originalAllCalcDataManager refers\n" +
" * to the aggregate assignment.\n" +
" *\n" +
" * @param asgnmtId The id of the assignment to load.\n" +
" * @return the original AllCalcDataManager for the given assignment Id\n" +
" */\n" +
" private AllCalcDataManager getOriginalAllCalcDataManager(GeneratedId asgnmtId) {\n" +
" if(originalAllCalcDataManager == null) {\n" +
" originalAllCalcDataManager = createOriginalAllCalcDataManager();\n" +
" updatedAllCalcDataManager = null;\n" +
" }\n" +
"\n" +
" if(asgnmtId.equals(originalAllCalcDataManager.getAsgnmtId())) {\n" +
" return originalAllCalcDataManager;\n" +
" }\n" +
"\n" +
" return originalAllCalcDataManager.getAllCalcDataManager(asgnmtId);\n" +
" }\n" +
"\n" +
" private AllCalcDataManager createOriginalAllCalcDataManager() {\n" +
" AllPolicies allPolicies = PolicyManager.getInstance().getAllPolicies();\n" +
" try {\n" +
" return AllCalcDataManager.createForAsgnmt(asgnmtMaster, allPolicies);\n" +
" }\n" +
" catch (Exception e) {\n" +
" throw new InternalApplicationException(\"Failed to load ACDM for assignment with id:\"+asgnmtMaster.getAsgnmt(), e);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Obtains the updated AllCalcDataManager for the given assignment Id. The assignment id must be given because in a\n" +
" * multiple assignment environment, a specific component needs to be requested, as originalAllCalcDataManager refers\n" +
" * to the aggregate assignment.\n" +
" *\n" +
" * @param asgnmtId The id of the assignment to load.\n" +
" * @return the updated AllCalcDataManager for the given assignment Id\n" +
" */\n" +
" private AllCalcDataManager getUpdatedAllCalcDataManager(GeneratedId asgnmtId) {\n" +
" if(updatedAllCalcDataManager == null) {\n" +
" throw new InternalApplicationException(\"Attempted to use the updated all calc data manager before it was created.\");\n" +
" }\n" +
"\n" +
" if(asgnmtId.equals(updatedAllCalcDataManager.getAsgnmtId())) {\n" +
" return updatedAllCalcDataManager;\n" +
" }\n" +
"\n" +
" return updatedAllCalcDataManager.getAllCalcDataManager(asgnmtId);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Obtains the updated AllCalcDataManager for the given assignment Id, if present. If not present, returns the\n" +
" * original ACDM instead. The assignment id must be given because in a\n" +
" * multiple assignment environment, a specific component needs to be requested, as originalAllCalcDataManager refers\n" +
" * to the aggregate assignment.\n" +
" *\n" +
" * @param asgnmtId The id of the assignment to load.\n" +
" * @return he updated AllCalcDataManager for the given assignment Id, if present. If not present, returns the\n" +
" * original ACDM instead.\n" +
" */\n" +
" AllCalcDataManager getLatestAllCalcDataManager(GeneratedId asgnmtId) {\n" +
" if (updatedAllCalcDataManager != null) {\n" +
" return getUpdatedAllCalcDataManager(asgnmtId);\n" +
" }\n" +
"\n" +
" return getOriginalAllCalcDataManager(asgnmtId);\n" +
" }\n" +
"\n" +
" //todo-ute: the method name is misleading as it may also create a new amended ACD if period is amendable by system on user's behalf\n" +
" private AllCalculationData getAllCalcData(AllCalcDataManager mgr, TimeSheetIdentifier id) {\n" +
" try {\n" +
" EmployeePeriodInfo epInfo = mgr.getEmployeePeriodInfo(id.getPpEnd());\n" +
" if (epInfo == null) {\n" +
" final PolicyID asgnmtPolicyProfile = mgr.getAsgnmtMaster().getPolicy_profile(id.getPpEnd());\n" +
" final PolicyID policyProfileId = (asgnmtPolicyProfile == null) ? PolicyID.EMPTY_POLICYID : asgnmtPolicyProfile;\n" +
" throw new InvalidPayPeriodException(\"EmployeePeriodInfo could not be obtained for employee \" +\n" +
" id.getEmployee() + \" and assignment \" + id.getAsgnmt() + \" and period end - \" + id.getPpEnd() + \". \" +\n" +
" \"Possibly because the employee is not active on that date or there are not enough initialized policy profile \" +\n" +
" \"periods for policy profile \" + policyProfileId );\n" +
" }\n" +
"\n" +
" TimeSheetCalculationInfo calcInfo = this.getCalculationInfo(mgr, id);\n" +
" AssignmentPeriodState asgnmtPeriodState = calcInfo.getAssignmentPeriodState();\n" +
" TimeSheetState timesheetState = calcInfo.getTimeSheetState();\n" +
" //If requesting EDIT_VERSION, check to see if one exists or that period is amendable by the logged in user\n" +
" if (id.getVersion() == EmployeePeriodVersionInfo.EDIT_VERSION && !epInfo.hasEditVersion()\n" +
" && !isPeriodAmendable(epInfo, mgr, asgnmtPeriodState, timesheetState) ) {\n" +
" throw new InternalApplicationException(\"An editable timesheet could not be obtained for employee \" +\n" +
" id.getEmployee() + \" and period end - \" + id.getPpEnd());\n" +
" }\n" +
"\n" +
" //Check to see if an editable ACD is in ACD manager's cache - if one exists, use it\n" +
" //An empty unmodifiable ACD for a prior period might be in cache if an ACD for prior timesheet is requested and\n" +
" //if that's the case, we want to still be able to create an amended ACD\n" +
" final AllCalculationData acd = mgr.getAllCalcData(id.getPpEnd(),id.getVersion(),AllCalculationData.FETCH_CACHED_ONLY);\n" +
" if (acd != null && !acd.isUnmodifiable()) {\n" +
" return acd;\n" +
" }\n" +
"\n" +
" //Create an amended ACD only if the period is amendable and the logged in user can amend it\n" +
" if (isAmendableBySystemForUser(epInfo, mgr, asgnmtPeriodState)) {\n" +
" final AllCalculationData amendedAllCalcData = mgr.createAmendedAllCalcData(epInfo, parms.getApp_user(), WDateTime.now());\n" +
" // No need to hook this up to approval change logic, since it is an in-memory change so far.\n" +
" //Recalculate the amended ACD so that timesheet exceptions (if any) get created on the amended timesheet\n" +
" amendedAllCalcData.recalc(mgr);\n" +
" }\n" +
"\n" +
" //By this time, the ACD mgr's ACD cache will have the amended ACD if one's created in this method\n" +
" //and we can retrieve the cached amended ACD or the ACD from the database\n" +
" return mgr.getAllCalcData(id.getPpEnd(), id.getVersion());\n" +
" } catch(InvalidPayPeriodException e) {\n" +
" //Rethrow InvalidPeriodExceptions--don't wrap them. We want them to be typed as InvalidPeriodExceptions.\n" +
" throw e;\n" +
" } catch (Exception e) {\n" +
" throw new InternalApplicationException(\"Timesheet could not be obtained for employee \" +\n" +
" id.getEmployee() + \", assignment \" + id.getAsgnmt()+ \", period end \" + id.getPpEnd() + \" and version \" + id.getVersion() + \".\", e);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * A period is amendable if one of the following is true:\n" +
" * <pre>\n" +
" * - {@link #isAmendableBySystemForUser(EmployeePeriodInfo, AllCalcDataManager, AssignmentPeriodState)\n" +
" * amendable by system on users's behalf}\n" +
" * - {@link TimeSheetState#isAmendable() amendable directly by user} generally by clicking \"Amend\" button on\n" +
" * prior active period with one or more closed timesheets\n" +
" * </pre>\n" +
" */\n" +
" private static boolean isPeriodAmendable(EmployeePeriodInfo epInfo, AllCalcDataManager mgr,\n" +
" AssignmentPeriodState asgnmtPeriodState, TimeSheetState timesheetState)\n" +
" throws SQLException, MultipleRowDbRecException {\n" +
" return timesheetState.isAmendable() || isAmendableBySystemForUser(epInfo, mgr, asgnmtPeriodState);\n" +
" }\n" +
"\n" +
" /**\n" +
" * A period is amendable by system on user's behalf (not the same as AUTO_AMEND or SYS_AMEND which are also created by\n" +
" * system) if user has amend rights for the specified period as of today and if specified period is a priod period\n" +
" * with no timesheet or it's a prior period timesheet with just system amended but not user amended\n" +
" * @param epInfo\n" +
" * @param mgr\n" +
" * @param asgnmtPeriodState\n" +
" * @return\n" +
" * @throws PolicyLookupException\n" +
" * @throws MultipleRowDbRecException\n" +
" * @throws SQLException\n" +
" * @see AssignmentPeriodState#isPeriodAmendable() \n" +
" * @see Approval_event_type#SYS_AMEND\n" +
" * @see Approval_event_type#AMEND\n" +
" */\n" +
" private static boolean isAmendableBySystemForUser(EmployeePeriodInfo epInfo, AllCalcDataManager mgr,\n" +
" AssignmentPeriodState asgnmtPeriodState)\n" +
" throws MultipleRowDbRecException, SQLException {\n" +
" return asgnmtPeriodState.isPeriodAmendable() && // TODO: this doesn't match the javadoc?\n" +
" ( mgr.isPriorModifiablePeriodWithNoTimesheet(epInfo) || epInfo.isPriorPeriodWithSysAmendVersionOnly() );\n" +
" }\n" +
"\n" +
" private AllCalculationData getOriginalAllCalcData(TimeSheetIdentifier timeSheetIdentifier) {\n" +
" return getAllCalcData(getOriginalAllCalcDataManager(timeSheetIdentifier.getAsgnmt()), timeSheetIdentifier);\n" +
" }\n" +
"\n" +
" private AllCalculationData getUpdatedAllCalcData(TimeSheetIdentifier timeSheetIdentifier) {\n" +
" return getAllCalcData(getUpdatedAllCalcDataManager(timeSheetIdentifier.getAsgnmt()), timeSheetIdentifier);\n" +
" }\n" +
"\n" +
"\n" +
" private Set<TimeSheetIdentifier> getTimeSheetIdentifiers() {\n" +
" return timeSheetIdentifiers;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns the pay period end for the period containing date provided, relative\n" +
" * to the defined periods for this assignment.\n" +
" *\n" +
" * @param aDateInUnknownPeriod any date we want to know the period for\n" +
" * @return never null\n" +
" */\n" +
" WDate getPpEndForDate(GeneratedId asgnmtId, WDate aDateInUnknownPeriod) {\n" +
" return TimeSchedUtils.getPpEndForDate(getOriginalAllCalcDataManager(asgnmtId), aDateInUnknownPeriod);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Assign system_record_id's to any DbRec objecs in the collection which lack them.\n" +
" * @param dbRecList List of DbRec objects on which to check the system_record_id. Records in list will be modified.\n" +
" */\n" +
" static private void assignSystemRecordIds(ListWrapBase dbRecList) {\n" +
" for (Iterator iterator = dbRecList.getCollection().iterator(); iterator.hasNext();) {\n" +
" DbRec dbRec = (DbRec) iterator.next();\n" +
" assignSystemRecordId(dbRec);\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Assign system_record_id to a DbRec object, if it is not set or contains a temporary id.\n" +
" * @param dbRec DbRec objects on which to check/change the system_record_id.\n" +
" */\n" +
" static private void assignSystemRecordId(DbRec dbRec) {\n" +
" if (dbRec.getSystem_record_id() == null || dbRec.getSystem_record_id().requiresPermanentId()) {\n" +
" dbRec.setSystem_record_id(SystemId.getNewID());\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Removes the last approval event (employee approval) from approval event list from the AllCalculationData object of\n" +
" * {@link #updatedAllCalcDataManager}\n" +
" * @param timeSheetId - Used to get the {@link AllCalculationData} object from {@link #updatedAllCalcDataManager}\n" +
" */\n" +
" void undoEmployeeApproval(TimeSheetIdentifier timeSheetId) {\n" +
" final AllCalculationData updatedAllCalcData = getUpdatedAllCalcData(timeSheetId);\n" +
" updatedAllCalcData.getApproval_eventList().removeLastApprovalEvent();\n" +
" }\n" +
"\n" +
" /**\n" +
" * Update the current object to be able to be used for other timesheets for this assignment\n" +
" * @param parms\n" +
" * @param timeSheetIdentifiers\n" +
" */\n" +
" public void update(TimeEntryParmsPerAssignment parms, Set<TimeSheetIdentifier> timeSheetIdentifiers) {\n" +
" setClassFields(parms, timeSheetIdentifiers);\n" +
" }\n" +
"\n" +
" private void setClassFields(TimeEntryParmsPerAssignment parms, Set<TimeSheetIdentifier> timeSheetIdentifiers) {\n" +
" this.parms = parms;\n" +
" this.timeSheetIdentifiers = TimeEntryParmsPerAssignment.TIMESHEET_IDENTIFIER_TREESET_FACTORY.newInstance();\n" +
" this.timeSheetIdentifiers.addAll(timeSheetIdentifiers);\n" +
" }\n" +
"\n" +
" /**\n" +
" * Recalculates and Saves timesheets (if saveData == true) for timesheet ids in {@link #parms} that belong to this\n" +
" * assignment\n" +
" *\n" +
" * @param trans\n" +
" * @return AggregateTimeEntryTransactionResults for all saved timesheets\n" +
" * @throws Exception in case of fatal error during timesheet recalc & save e.g. errors while store data in DB etc.\n" +
" * This precludes ConcurrentUserModification exception. Any exception thrown from this method, eventually gets\n" +
" * escalated as transaction exception\n" +
" */\n" +
" public TimeEntryTransactionResults recalcAndSaveTimesheets(TimeEntryTransaction trans, boolean saveData) throws Exception {\n" +
" // TODO-13285: Remove this code and implement merging concurrently modified time sheets\n" +
" AllCalcDataManager tempAcdMgr = null;\n" +
" AggregateTimeEntryTransactionResults results = (AggregateTimeEntryTransactionResults) parms.getTimeEntryResultsFactory().newInstance();\n" +
" Set<TimeSheetIdentifier> timeSheetIds = getAffectedTimeSheetIdentifiers(trans, parms);\n" +
" int retryCount = 0;\n" +
" boolean canRetryConcurrentModification = true;\n" +
" if (timeSheetIds.isEmpty()) {\n" +
" return results;\n" +
" }\n" +
"\n" +
" assert timeSheetIdentifiers.containsAll(timeSheetIds) : \"Attempted to recalculate a timesheet identifier not managed by this AssignmentManager.\";\n" +
" createUpdatedAllCalcDataManager(timeSheetIds);\n" +
"\n" +
" TransactionStatus timeSheetSaveStatus = null;\n" +
" Map<TimeSheetIdentifier, TimeSheetTransactionApplier> applyResultsMap = new HashMap<TimeSheetIdentifier, TimeSheetTransactionApplier>();\n" +
" for (TimeSheetIdentifier timeSheetId : timeSheetIds) {\n" +
" TimeSheetTransactionApplier applyResults = applyTransaction(timeSheetId, trans);\n" +
" if(applyResults.getApprovalEventType() != Approval_event_type.SAVE_SCHEDULE) {\n" +
" applyResultsMap.put(timeSheetId, applyResults);\n" +
" }\n" +
"\n" +
" recalc(timeSheetId, applyResults.getApprovalEventType());\n" +
"\n" +
" boolean isWithdrawal =\n" +
" (trans.findTransactionApprovalEvent(timeSheetId, Approval_event_type.WITHDRAWAL) != null);\n" +
" if (!isWithdrawal) {\n" +
" // There are 3 conditions identified in the following if statement that can prevent an \"action\" on the\n" +
" // current timesheet. The first is an exception that prevents save. In this case, we should never allow a\n" +
" // save of the timesheet to occur. The second condition is an exception that prevents submit. In this\n" +
" // condition, we should only allow a save if the user is not attempting to submit (apply an APPROVAL event).\n" +
" // The third case is errors that occur while applying transactions to the timesheet. An example of when this\n" +
" // may occur is if the same time sheet detail row is modified by 2 users. In all of these cases we prevent\n" +
" // the save operation from executing.\n" +
" // In the next condition where we check if an error has occurred, we remove the last approval event from the\n" +
" // timesheet if we did not save AND it was a submit (APPROVAL) event. Transactions only allow 1 event to be applied\n" +
" // to the timesheet. The transaction explicitly excludes SAVE_TIME_SHEET and SAVE_SCHEDULE events from being applied.\n" +
" // It relies on these events being applied only by the save operation itself. As a result, a maximum of 2 events may\n" +
" // be added to the timesheet as a result of the transaction (the event on the transaction if it was not a save event)\n" +
" // and the SAVE_TIME_SHEET or SAVE_SCHEDULE event added during the save operation.\n" +
"\n" +
" if (hasSubmitEvent(trans, timeSheetId) &&\n" +
" hasExceptionsPreventingSubmit(timeSheetId)) {\n" +
" timeSheetSaveStatus = getHighestPriorityError(timeSheetSaveStatus, Messages.EXCEPTIONS_PREVENT_SUBMIT);\n" +
" }\n" +
"\n" +
" if (hasExceptionsPreventingSave(timeSheetId)) {\n" +
" timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.ERROR,\n" +
" Messages.EXCEPTIONS_PREVENT_SAVE.getLabel());\n" +
" }\n" +
" }\n" +
"\n" +
" if (applyResults.hasErrors()) {\n" +
" timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.ERROR,\n" +
" Messages.TIME_SHEET_EXCEPTION_JAVA_EXCEPTION .getLabel());\n" +
" }\n" +
" }\n" +
"\n" +
"\n" +
" boolean errorPreventsAction = (timeSheetSaveStatus != null);\n" +
"\n" +
"\n" +
" Approval_event lastOriginalApprovalEvent = null;\n" +
" for (TimeSheetIdentifier timeSheetId : timeSheetIds) {\n" +
" TimeSheetTransactionApplier applyResults = applyResultsMap.get(timeSheetId);\n" +
" try {\n" +
" while (saveData && canRetryConcurrentModification) {\n" +
" if (errorPreventsAction) {\n" +
" // If the changes were not saved due to errors, and this operation (transaction) was an employee timesheet submission,\n" +
" // remove the approval event from the unsaved ACD.\n" +
" // NOTE: We do not have to concern ourselves with removing SAVE_TIME_SHEET or SAVE_SCHEDULE events here as these events\n" +
" // are explicitly EXCLUDED in the TimeSheetTransactionApplier (not applied) and are applied by the save operation (which\n" +
" // has not executed if we made it here.\n" +
" if (hasSubmitEvent(trans, timeSheetId)) {\n" +
" undoEmployeeApproval(timeSheetId);\n" +
" }\n" +
" } else {\n" +
" save(timeSheetId, applyResults.getApprovalEventType());\n" +
" timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.SAVED,\n" +
" Messages.TIME_SHEET_SAVED.getLabel());\n" +
" postSave();\n" +
" }\n" +
" // All operations completed, so retries are no longer necessary\n" +
" canRetryConcurrentModification = false;\n" +
" }\n" +
" } catch (ConcurrentUserModificationException ex) {\n" +
" // Store the old AcdMgr, when a concurrent mod error due to another user modifying a time sheet occurs we want\n" +
" // to receive the same new approval events to ensure that the user is forced to reload the time sheet to continue.\n" +
" // TODO-13285: Remove the tempAcdMgr and make merging concurrently modified time sheets possible,\n" +
" // TODO-13285: requires considering multiple edge cases for the user interface\n" +
" if (tempAcdMgr == null) {\n" +
" tempAcdMgr = getOriginalAllCalcDataManager(asgnmtMaster.getAsgnmt());\n" +
" }\n" +
"\n" +
" if (retryCount == MAX_RETRIES) {\n" +
" // return the acdMgr to its previous state, this ensures the user must reload the time sheet\n" +
" // TODO-13285: Remove this code and implement merging concurrently modified time sheets\n" +
" originalAllCalcDataManager = tempAcdMgr;\n" +
" GeneratedId errorId = ServerErrorLogger.singleton.log(new ServerError(\"Exception saving time sheet\", ex,\n" +
" Program_source.SERVER_REQUEST, parms.getApp_user().getLogin_id()));\n" +
" cat.error(\"Unable to save time sheet. debug_error_log id:\" + errorId, ex);\n" +
" //Use timeSheetSaveStatus to send concurrent error message to user.\n" +
" errorPreventsAction = true;\n" +
" timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.ERROR,\n" +
" Messages.TIME_SHEET_EXCEPTION_CONCURRENT_USER.getLabel());\n" +
" // We have retried saving and have hit the MAX_RETRIES threshold, so stop retrying.\n" +
" canRetryConcurrentModification = false;\n" +
" } else {\n" +
" // On the first retry, we need to capture the last (most recent) approval event that existed on this timesheet prior to applying\n" +
" // any transactions because when we reload the \"original\" ACD to attempt to reapply the transactions we will lose track\n" +
" // of this (we'll pull in approval events for transactions that were saved in other sessions/processes). This is necessary to\n" +
" // allow more than 1 retry\n" +
" if (lastOriginalApprovalEvent == null) {\n" +
" lastOriginalApprovalEvent = getAllCalcData(getOriginalAllCalcDataManager(timeSheetId.getAsgnmt()), timeSheetId).getApproval_eventList().getLastApprovalEvent();\n" +
" }\n" +
" retryCount++;\n" +
" // attempt to reapply the transaction\n" +
" try {\n" +
" applyResults = attemptTimeSheetTransactionReapply(trans, timeSheetId, lastOriginalApprovalEvent);\n" +
" } catch (ConcurrentUserModificationException cume) {\n" +
" // return the acdMgr to its previous state, this ensures the user must reload the time sheet\n" +
" // TODO-13285: Remove this code and implement merging concurrently modified time sheets\n" +
" originalAllCalcDataManager = tempAcdMgr;\n" +
" // if we were unable to reapply transactions, no further retries are necessary (because it will just keep failing)\n" +
" // subsequent retries only succeed if we were able to reapply the new transactions successfully and still received\n" +
" // another ConcurrentUserModificationException (a second change occurred while applying transactions)\n" +
" canRetryConcurrentModification = false;\n" +
" errorPreventsAction = true;\n" +
" timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.ERROR,\n" +
" Messages.TIME_SHEET_EXCEPTION_CONCURRENT_USER.getLabel());\n" +
" }\n" +
" }\n" +
" }\n" +
" results.add(getDiff(timeSheetId, trans, applyResults, timeSheetSaveStatus, !errorPreventsAction));\n" +
" }\n" +
" return results;\n" +
" }\n" +
"\n" +
" ";
@NonNls String part2 = "\n" +
" private TransactionStatus getHighestPriorityError(TransactionStatus existingStatus, Message newError) {\n" +
" if(existingStatus == null) {\n" +
" return newError;\n" +
" }\n" +
" TransactionStatus timeSheetSaveStatus = new TransactionStatusImpl(Transaction_status.ERROR,\n" +
" );\n" +
" return timeSheetSaveStatus;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns true if the given TimeEntryTransaction has a submit event for the given time sheet.\n" +
" * @param trans TimeEntryTransaction to search\n" +
" * @param timeSheetId time sheet to search\n" +
" * @return true if a submit event exists\n" +
" */\n" +
" private static boolean hasSubmitEvent(TimeEntryTransaction trans, TimeSheetIdentifier timeSheetId) {\n" +
" return trans.findTransactionApprovalEvent(timeSheetId, Approval_event_type.APPROVAL) != null;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Resolves modifications that impact only non-\"input\" values on the timesheet to prevent them\n" +
" * from causing ConcurrentUserModificationExceptions on subsequent attempts at saving the data\n" +
" * @param trans time entry transaction to resolve\n" +
" * @param id time sheet ID\n" +
" * @param lastApprovalEvent the most recent approval event on the timesheet prior to any transactions\n" +
" * @return TimeSheetTransactionApplier if transactions were reapplied\n" +
" * @throws Exception on error loading the acd\n" +
" */\n" +
" private TimeSheetTransactionApplier attemptTimeSheetTransactionReapply(final TimeEntryTransaction trans, final TimeSheetIdentifier id, Approval_event lastApprovalEvent) throws Exception {\n" +
" // Clean out the ACDM cache to force reloads of the data from the database\n" +
" invalidateAllCalcDataManagers();\n" +
" AllCalculationData originalAcd = getAllCalcData(getOriginalAllCalcDataManager(id.getAsgnmt()), id);\n" +
" // Get any new approval events that exist in the database\n" +
" Approval_eventList newApprovalEvents = originalAcd.getApproval_eventList().getApprovalsSince(lastApprovalEvent);\n" +
" // Check if a \"concurrent save\" is possible given the events that have occurred\n" +
" if (newApprovalEvents.canConcurrentSave()) {\n" +
" //TODO:FIX\n" +
" //createUpdatedAllCalcDataManager(listOfAllIdsHere);\n" +
" TimeSheetTransactionApplier applyResults = applyTransaction(id, trans); // reapply transactions\n" +
" recalc(id, applyResults.getApprovalEventType()); // recalculate\n" +
" DbRecTime_sheet originalTimeSheet = originalAcd.getTime_sheet();\n" +
" DbRecTime_sheet updatedTimeSheet = getAllCalcData(getUpdatedAllCalcDataManager(id.getAsgnmt()), id).getTime_sheet();\n" +
" // Copy the system fields from the original time sheet to the updated time sheet because\n" +
" // we have determined at this point that the concurrent save is okay based on the difference\n" +
" // between these 2 time sheets. Copying these fields over will allow the save to complete\n" +
" // without causing a ConcurrentUserModificationException by making the update counter match.\n" +
" DbRecFieldCopier copier = new DbRecFieldCopier(DataDictionary.getCltnOfSystemFieldNames());\n" +
" copier.copyFields(originalTimeSheet, updatedTimeSheet);\n" +
" return applyResults;\n" +
" } else {\n" +
" // Otherwise, throw the exception\n" +
" throw new ConcurrentUserModificationException(\"Unable to save time sheet for assignment \" + originalAcd.getAsgnmtId() + \"due to concurrent changes.\");\n" +
" }\n" +
" }\n" +
"\n" +
" /**\n" +
" * Returns a set of AssignmentManagers that are affected by changes in the given transaction and managed by this\n" +
" * TimeEntryManager.\n" +
" *\n" +
" * @return a set of AssignmentManagers that are affected by changes in the given transaction and managed by this\n" +
" * TimeEntryManager.\n" +
" */\n" +
" public Set<TimeSheetIdentifier> getAffectedTimeSheetIdentifiers(TimeEntryTransaction transaction, TimeEntryParmsPerAssignment parms) {\n" +
" Set<TimeSheetIdentifier> affectedTimeSheetIds = new HashSet<TimeSheetIdentifier>();\n" +
" for(GeneratedId assignmentId : getAssignmentIds()) {\n" +
" for(TimeSheetIdentifier timeSheetId : parms.getTimeSheetIdentifiersForAssignment(assignmentId)) {\n" +
" if(!transaction.obtainAllRows(timeSheetId).isEmpty()) {\n" +
" affectedTimeSheetIds.add(timeSheetId);\n" +
" }\n" +
" }\n" +
" }\n" +
" return affectedTimeSheetIds;\n" +
" }\n" +
"\n" +
" /**\n" +
" * Obtains all assignment id's that are managed by this AssignmentManager.\n" +
" *\n" +
" * @return all assignment id's that are managed by this AssignmentManager.\n" +
" */\n" +
" public Set<GeneratedId> getAssignmentIds() {\n" +
" Set<GeneratedId> assignmentIds = new HashSet<GeneratedId>();\n" +
" Asgnmt_masterList amList = asgnmtMaster.getCompAsgnmtMasters();\n" +
" for(Asgnmt_master am : amList) {\n" +
" assignmentIds.add(am.getAsgnmt());\n" +
" }\n" +
" return assignmentIds;\n" +
" }\n" +
"\n" +
" public GeneratedId getEmployee() {\n" +
" return asgnmtMaster.getEmployee();\n" +
" }\n" +
"\n" +
" /** Single or aggregate assignment master associated with this manager */\n" +
" private final Asgnmt_master asgnmtMaster;\n" +
"\n" +
" private TimeEntryParmsPerAssignment parms;\n" +
"\n" +
" /** Set of {@link TimeSheetIdentifier}'s which are managed by this object.\n" +
" * Composed of all the identifiers in the TimeEntryParms for this assignment and related components.\n" +
" */\n" +
" private Set<TimeSheetIdentifier> timeSheetIdentifiers;\n" +
"\n" +
" /**\n" +
" * Original Single or Aggregate ACDM, as loaded from the database. ACD's in here are not modified.\n" +
" * This ACDM is lazily loaded, and should always be obtained from {@link #getOriginalAllCalcDataManager(GeneratedId)}.\n" +
" *\n" +
" * Null means that the ACDM has not yet been loaded from the database, and any attempts to use it should first\n" +
" * initialize it. Note: The meaning of null is different than updatedAllCalcDataManager's null meaning.\n" +
" */\n" +
" private AllCalcDataManager originalAllCalcDataManager = null;\n" +
"\n" +
" /**\n" +
" * Updated Single or Aggregate ACDM--a shallow copy of originalAllCalcDataManager. ACD's in here are modified by\n" +
" * TimeEntryTransactions. This ACDM is NOT lazily loaded--null means no changes present, and non-null means the\n" +
" * updated ACDM has been explicitly created.\n" +
" *\n" +
" * Null means that no changes have been made to the ACDM or data, and any attempts to use it should either throw a\n" +
" * descriptive error, or explicitly create the updatedAllCalcDataManager.\n" +
" * Note: The meaning of null is different than originalAllCalcDataManager's null meaning.\n" +
" */\n" +
" private AllCalcDataManager updatedAllCalcDataManager = null;\n" +
"\n" +
" private static final int MAX_RETRIES = 2;\n" +
"\n" +
" private static final Category cat=Category.getInstance(AssignmentManager.class.getName());\n" +
"}";
configureFromFileText("Foo.java", part1 + part2);
final PsiDocumentManager docManager = PsiDocumentManager.getInstance(ourProject);
final Document doc = docManager.getDocument(myFile);
doc.insertString(part1.length(), "/**");
boolean old = DebugUtil.CHECK;
DebugUtil.CHECK = true;
try {
docManager.commitAllDocuments();
}
finally {
DebugUtil.CHECK = old;
}
}
}