Summer Headers and Trailers

Use the Standard Academic Year (SAY) Summer Configurator to define when a student population should have a summer term treated as a Header or Trailer.

Overview

The Summer SAY Configurator uses groovy scripts to determine when to award a Summer Term and whether to treat it as a Header or Trailer. However, you can always manually add a summer term as a Header, Trailer, or remove a summer term for a student using the UI.

What Is a Summer Term

Summer terms are either before an upcoming academic year as Header or after a completed academic year as a Trailer. Summer terms are optional and most students attend them voluntarily if they want to progress quicker than normal or to repeat courses for a better grade. You can decide if you how you want to treat the summer term as its placement impacts students fund eligibility.

FasSafiSyncEventV2 (SAFI) Message: SummerTerm Element

The Summer Term element in the SAFI is nested within the <Term> Element, but after the <AcademicYears> element because:
  • It isn't technically part of the Academic Year.
  • It can be added as a Header or Trailer based on the configuration.
The Summer Term should contain all <TermPeriods> eligible to be Summer Terms regardless if the student is attending that term or not. Also, it can't overlap with any other term.

Oracle People Soft Campus Solutions delivers Summer Term integration with the product. he <SummerTerm> element of the SAFI message includes terms with TERM_TBL - TERM_CATEGORY = “Summer Term”.

SummerTerm Element

 <Term>
   <TermType>Semester</TermType>
   <AcademicYears>
      <AcademicYear>
         <AcademicYearNumber>1</AcademicYearNumber>
         <StartDate>2018-08-28</StartDate>
         <EndDate>2019-05-16</EndDate>
         <TermPeriods>
            <TermPeriod>
               <Description>Fall Semester, 2018</Description>
               <StartDate>2018-08-28</StartDate>
               <EndDate>2018-12-13</EndDate>
            </TermPeriod>
            <TermPeriod>
               <Description>Spring Semester, 2019</Description>
               <StartDate>2019-01-22</StartDate>
               <EndDate>2019-05-16</EndDate>
            </TermPeriod>
        </TermPeriods>
      </AcademicYear>
   </AcademicYears>
   <SummerTerm>
      <!--A Summer Term will be outside of the normal Academic Years sent in the SAFI as it could be a header or trailer (not always up to the SIS)-->
      <TermPeriods>
         <!--Term period element will be identical to the existing ones.  Summer terms must not overlap other terms-->
         <TermPeriod>
            <Description>Summer Term 2018</Description>
            <StartDate>2018-06-15</StartDate>
            <EndDate>2018-08-15</EndDate>
         </TermPeriod>
         <TermPeriod>
            <Description>Summer Term 2019</Description>
            <StartDate>2019-06-10</StartDate>
            <EndDate>2019-08-05</EndDate>
         </TermPeriod>
      </TermPeriods>
   </SummerTerm>
</Term>

Configurable Attributes

Configure the Summer Term as a Header or Trailer.

Using groovy script, you can configure the application to add a Summer Term automatically as a Header or a Trailer if there is enrollment in the term. The configuration to control this behavior has two controls, first the CSV file and second, the groovy script.

  1. The CSV File
    1. PKGSCHEDATTRIB.csv
    2. Column: SUMMER_TERM_SCRIPT (this is the file path)
    3. Data Type: Groovy Script
  • Level: Set per OPEID
    • The Groovy Script

The following code block is a simple sample Groovy Script that would return a Summer Term as a Summer Trailer.

Sample Summer Trailer Groovy Script

return "trailer";

Summer Header or Trailer Groovy Script: Explained

You have complete flexibility through Groovy Scripting to treat Summer Terms as Headers or Trailers globally or for very specific student populations.

Review the following table to see an example of a few simple scripts and their descriptions.

Sample Groovy Script Description
return "header"; The application automatically defaults students with an enrollment in a Summer Term to be a Summer Header
return "trailer";

The application automatically defaults students with an enrollment in a Summer Term to be a Summer Trailer

If the Summer Term is before the first academic year, then the Summer Term is considered a Header

null

The application automatically defaults students with an enrollment in a Summer Term to be a Summer Trailer

If the Summer Term is before the first academic year, then the Summer Term is considered a Header

Configure When Summer Terms Are Awarded

Using groovy script, you can configure:
  • When to start awarding a Summer Term. For example, 20 days before Spring Term Ends plus enrollment in the Summer Term.

  • When to freeze the Fall/Spring awards and roll over the remaining eligibility for Direct Loans to the Summer Term plus any additional funds. For example, grade level increases.

You have complete flexibility as to when you want to start packaging a Summer Term through groovy configuration. The delivered Baseline Configuration packages a Summer Term when the Standard Academic Year has ended, but you can package the Summer Trailer much earlier if you want, but modifying the Groovy Script provided through the Baseline Configuration.

The configuration to control this behavior has two controls, first the CSV file and second, the groovy script.

  1. The CSV file:
    1. FAS_FUND_CONFIG.csv
    2. Column: AWARD_OVERRIDE_CRITERIA
    3. Data Type: CLOB (Groovy Script)
    4. Level: Set per Fund
    5. Possible Values:
  2. The Groovy Script

The following code block is a copy of the Baseline Configuration Groovy Script for Direct Subsidized Loans (DSUB) and Direct Unsubsidized Loans (DUNSUB).

Full Baseline Configuration Groovy Script Expand source

/* Baseline - Award Overriding Criteria for DSUB DUNSUB - combined Term and Non-Term */
import org.joda.time.LocalDate;
optimizer
         .setCumulativeGradeLevelLimitApplicable(
                 (term == null || term.isStandard())
                 && (program.isUndergraduate() || !isLastAcy))
         .setFundingForPeriodFilter {fundCode, fundType -> fundCode != "DISCOUNT"};
if (pp.getStatus() == "CANCELED")
{
    return awardInfo.withMaxAmount(0).withRetainedAmount(0);
}
if (program.isTerm())
{
    def overlappingTerms = primaryProgram.getTerms().getOverlappingWith(term);
    /* Methods returning collection, as well as filters, will be plural; otherwise singular */
    if (overlappingTerms.getStudentsTermStatuses().contains("WITHDRAWN")) 
    {
        log.debug("STUDENT TERM IS WITHDRAWN");
    
        def r2t4OverlappingTerms = r2t4.getOverlappingWith(term)
                .getWithProcessStatuses(["NOT_REQUIRED", "COMPLETED"])
                .getWithOldas(overlappingTerms.getOldas());
            
        if (!r2t4OverlappingTerms.isEmpty())
        {
            log.debug("R2T4 Overlapping Terms isn't empty, FREEZING!");
        
            def sum = disbursements.getInAcademicYear(acyNo)
                    .getWithFundCode(fundCode)
                    .getInAwardYear(awy)
                    .getInLoanPeriod(lpNo)
                    .getInPaymentPeriod(ppNo)
                    .filterStatuses { status -> status != "DISBURSEMENT_CANCELED" }
                    .getPpMaxDisbursementAmount();
                
            return awardInfo.withMaxAmount(sum).withRetainedAmount(sum);
        } 
        else
        {
            log.debug("R2T4 PROCESS NOT YET IN A FREEZING STATE");
        }
    }
    
    if (enrollmentStatus == "NOT_ATTENDING" || enrollmentStatus == "LESS_THAN_HALF_TIME" || (sapStatus != null && sapStatus == "FD_FINANCIAL_AID_DISQUALIFICATION"))
    {
                 log.debug("ZEROING - enrollmentStatus={}, sapStatus={} ", enrollmentStatus, sapStatus);
        return awardInfo.withMaxAmount(0).withRetainedAmount(0);
    }
    
         def freezingDateForStandard = acy.getTerms()
                 .filter {t -> t.isSummerTrailer() && t.isAttending() } 
                 .min() // get the earliest (yields an optional value)
                 .map {t -> t.getStartDate()} // map optional to its start date
                 .getOrElse(lp.getEndDate()); // if optional absent (no summer trailers) take lp.getEndDate
def now = LocalDate.now();
    if (log.debug(lp.getEndDate() <= now, "LOAN PERIOD IN THE PAST") // Update this line to determine when to freeze the standard terms and roll over unused funds to summer terms
         || log.debug(term.isStandard() && freezingDateForStandard <= now, "SUMMER TRAILER STARTING")
         || log.debug(term.isSummerHeader(), "SUMMER HEADER")
         || log.debug(term.isSummerTrailer() && now < freezingDateForStandard, "TOO EARLY FOR SUMMER TRAILER"))
    {
        def sum = disbursements.getInAcademicYear(acyNo)
               .getWithFundCode(fundCode)
               .getInAwardYear(awy)
               .getInLoanPeriod(lpNo)
               .getInPaymentPeriod(ppNo)
               .filterStatuses { status -> status != "DISBURSEMENT_CANCELED" }
               .getTotalDisbursementAmount();
         log.debug("FREEZING TO {}", sum);               
         return awardInfo.withMaxAmount(sum).withRetainedAmount(sum);
    }
}
return awardInfo; 

Summer Term Section of the Groovy Script: Explained

Using the Baseline Configuration Groovy Script for Direct Subsidized Loans (DSUB) and Direct Unsubsidized Loans (DUNSUB), the application doesn't award or package the student for loans until the Standard Academic Year (SAY) ends. For example, when both the Fall and Spring Terms are complete.

Once the Academic Year ends, the application lowers the awards for the Fall and Spring to match the sum of the disbursements. For example, down to the accepted amount. Then, the application rolls any remaining eligibility for the loan period over to the Summer Term (Summer Trailer).

This code block is the portion of the Baseline Configuration Groovy Script for DSUB and DUNSUB that is associated with packaging a Summer Term.

Summer Term Section of the Groovy Script

 def freezingDateForStandard = acy.getTerms()
                 .filter {t -> t.isSummerTrailer() && t.isAttending() } 
                 .min() // get the earliest (yields an optional value)
                 .map {t -> t.getStartDate()} // map optional to its start date
                 .getOrElse(lp.getEndDate()); // if optional absent (no summer trailers) take lp.getEndDate
def now = LocalDate.now();
    if (log.debug(lp.getEndDate() <= now, "LOAN PERIOD IN THE PAST") // Update this line to determine when to freeze the standard terms and roll over unused funds to summer terms
         || log.debug(term.isStandard() && freezingDateForStandard <= now, "SUMMER TRAILER STARTING")
         || log.debug(term.isSummerHeader(), "SUMMER HEADER")
         || log.debug(term.isSummerTrailer() && now < freezingDateForStandard, "TOO EARLY FOR SUMMER TRAILER"))
    {
        def sum = disbursements.getInAcademicYear(acyNo)
               .getWithFundCode(fundCode)
               .getInAwardYear(awy)
               .getInLoanPeriod(lpNo)
               .getInPaymentPeriod(ppNo)
               .filterStatuses { status -> status != "DISBURSEMENT_CANCELED" }
               .getTotalDisbursementAmount();
         log.debug("FREEZING TO {}", sum);               
         return awardInfo.withMaxAmount(sum).withRetainedAmount(sum);
    }
}
return awardInfo;
  1. FREEZING A TERM means the application is setting its award for the specific fund under evaluation to the Total Disbursement Amount of non-cancelled disbursements for that term; usually one disbursement. This also works for Summer Terms without disbursements, in which case their awards are frozen to $0.00. When the term is frozen, its award isn't subject to fund optimization. This means that it is taken into consideration, affecting other term awards that aren't frozen, but isn't amenable to change. Freezing a term is done in the then part of the last if statement:

    Section of Sample Groovy Script: DSUB and DUNSUB

            def sum = disbursements.getInAcademicYear(acyNo)
                   .getWithFundCode(fundCode)
                   .getInAwardYear(awy)
                   .getInLoanPeriod(lpNo)
                   .getInPaymentPeriod(ppNo)
                   .filterStatuses { status -> status != "DISBURSEMENT_CANCELED" }
                   .getTotalDisbursementAmount();
              log.debug("FREEZING TO {}", sum);               
              return awardInfo.withMaxAmount(sum).withRetainedAmount(sum);
  2. freezingDateForStandard:The main dynamics being implemented here is this: Summer Headers are always frozen and then either Standard Terms or Summer Trailers are frozen. Summer Trailers remain frozen to $0.00 when they are in the future. When the first Summer Trailer in an academic year has passed, Standard Terms are frozen, and the application allows the Summer Trailer to receive remaining fund eligibility due to fund optimization. So, an important variable is freezingDateForStandard, defined as:

    Section of Sample Groovy Script: DSUB and DUNSUB

     def freezingDateForStandard = acy.getTerms()
               .filter {t -> t.isSummerTrailer() && t.isAttending() } // from summer trailers (probably at most 1)
               .min() // get the earliest (yields an optional value)
               .map {t -> t.getStartDate()} // map optional to its start date
               .getOrElse(lp.getEndDate()); // if optional absent (no summer trailers) take lp.getEndDate

    freezingDateForStandard is defined as the Start Date of the earliest Summer Trailer (hence min()) having attendance in that academic year. Or, in the event there are no Summer Trailers, the application defaults to lp.getEndDate(), which is the end date of the respective Loan Period. Now all the relevant freezing rules appear in the condition through the last if statement, namely:

    Section of Sample Groovy Script: DSUBare and DUNSUB

         if (log.debug(lp.getEndDate() <= now, "LOAN PERIOD IN THE PAST") 
                || log.debug(term.isStandard() && freezingDateForStandard <= now, "SUMMER TRAILER STARTING")
                || log.debug(term.isSummerHeader(), "SUMMER HEADER")
                || log.debug(term.isSummerTrailer() && now < freezingDateForStandard, "TOO EARLY FOR SUMMER TRAILER"))
  3. The Respective LOAN PERIOD IS IN THE PAST: This happens for any terms if the respective Loan Period is in the past that is being frozen. This is what freezes earlier terms in donut scenarios. A donut scenario is when a student attends Term 1, does not attend Term 2, and returns in Term 3.
  4. The SUMMER TRAILER STARTING: In this condition, you can see term.isStandard() so the term under evaluation is a Standard Term and the current date is past the freezingDateForStandard (if this happens to be lp.getEndDate() this condition is equivalent to #3). Therefore, the application freezes the Standard Term and rolls resources over to the Summer Trailer.
  5. PART 5 - SUMMER HEADER: For this condition, the application freezes all Summer Headers regardless. If this condition is removed, Summer Headers are awarded just like Standard Terms and compete for available resources. Summer Headers are frozen to $0.00 unless there are disbursements for them.
  6. PART 6 - TOO EARLY FOR SUMMER TRAILER: This is exactly what prevents Summer Trailers from being awarded early and competing for resources with Standard Terms. In this condition, you can see term.isSummerTrailer() so the term under evaluation is a Summer Trailer and it is in the future, so the application freezes it and the value is $0.00 unless there are disbursements.
    Tip: #4 and #6 are mutually exclusive. When you freeze Standard Terms, you aren't freezing Summer Trailers unless #3 applies.

Other Configuration Considerations

  1. Months Calculation: You may want to analyze your institution's months configuration and update it to calculate the designated number of months for Summer Terms (See lines 10-12 in the sample below).
    1. Table: FAS_PACK_SCHED_CONF
    2. Filed: PP_MONTHS_SCRIPT

    Sample Months Calculation Groovy Script

    import org.joda.time.LocalDate;
    def returnValue = 0.0;
    if (program.getTermType() == null)
    {
        log.debug("PP MONTHS CALCULATION CONFIG: ERROR, null value passed from API");
        return 0.0;
    }
    log.debug("PP MONTHS CALCULATION CONFIG: Program Term Type = {}", program.getTermType());
    def programTermType = program.getTermType();
    def safiTerm = program.getTerms().getOverlappingWith(term).get(0);
    if (safiTerm.isSummer()) {
        returnValue = 2.0;
    }
    // Standard from this point on
    else if (programTermType == "Semester")
    {
        returnValue = 4.5;
    }
    else if (programTermType == "Trimester")
    {
        returnValue = 3.0;
    }
    else if (programTermType == "Quarter")
    {
        returnValue = 3.0;
    }
    return returnValue;
  2. Cost of Attendance Calculation: You may want to update your institution's cost of attendance for Summer Terms if it differs from Standard Terms
  3. Enrollment Status Configuration: You may want to update the enrollment status configuration if the enrollment status calculation is different for Summer Terms
  4. Other Areas: Anytime your institution treats Summer Terms differently than Standard Terms

Notable Application Behavior

  1. What if there are no courses in the Summer Term?
    1. The application packages the student according to the Academic Years and Terms sent in the SAFI and ignores the Summer Terms.
  2. What if there are two Summer Terms and the student is taking the second as a Trailer?
    • The application adds the second Summer Term as a Trailer and doesn't package the student with funds for the first Summer Term.
  3. What if there is no SUMMER_TERM_SCRIPT configured?
    1. If the summer term exists before Academic Year 1, and the Summer Term has enrollment, the application defaults the Summer Term to be a Header.
    2. If the summer term exists after Academic Year 1 and there is Summer Term enrollment, the application defaults the Summer Term to be a Trailer.
  4. What if the groovy script associated with the PKGSCHEDATTRIB.csv column SUMMER_TERM_SCRIPT is malformed?
    1. The application acts as if there is no SUMMER_TERM_SCRIPT configured and logs an APP_EVT warning message to notify you of the malformed groovy.
  5. What happens if I Manually update a Summer Term?
    • If you manually modify the configured behavior of a Summer Term, the application ignores the default configuration for that Summer Term moving forward.
Note: If you have manually overridden an award/fund for a term for which you later need to remove/move, you should:
  1. Reduce the award down to $0.00 manually.
  2. Remove the Term from the Academic Year.
  3. Add the term to the new Academic Year (if applicable).
  4. Increase the fund in the new Academic Year manually, if applicable.

Required Role Permissions

The user must have a role with the following General Permissions through Roles Management in order to view, add, edit or remove a Summer Term:

  • Student.
  • Manage Summer Term.
  • Package Fund Edit.

See Set General Permissions Matrix for additional information.

Navigation to the SFP User Interface Functionality

To view the Packaging Details:

SFP User Interface > Student > Financial Information > Packaging

To view the Disbursing Details:

SFP User Interface > Student > Financial Information > Disbursing