Oracle Cloud Time & Labor · Fast Formula Deep Dive
California Straight Time & Unpaid Time Rules: Reconciling Weekly Schedules & Shortfalls
Leveraging HWM array iterations to evaluate worked hours against published schedule thresholds and automate unpaid variance tracking
1. The California Compliance Context
Under California wage regulations, standard work arrangements often contend with complex Alternative Workweek Schedules (AWS), collective bargaining rules, or nonexempt salaried arrangements (such as 32, 35, or 37.5 scheduled weekly hours). In these setups, straight-time evaluation cannot simply rely on a fixed 40-hour threshold:
- Contractual Straight Time: Regular worked time is protected up to the employee's assigned schedule for that week (e.g., 37.5 hours). Any additional hours worked up to statutory thresholds must be classified as a distinct Straight Time attribute (
OUT_MEASURE_STRAIGHT_TIME). - Absence Credit Protection: Approved absence hours (e.g., California Paid Sick Leave or Vacation) must count toward satisfying the weekly schedule requirement, preventing employees from losing straight time entitlement when taking authorized leaves.
- Unpaid Time Deficit Calculation: When an employee fails to log their full scheduled hours and lacks paid leave to bridge the gap, the unworked shortfall must be quantified as Unpaid Time (
OUT_MEASURE_UNPAID_TIME) to facilitate automatic salary docking or compliance tracking.
The formula OTL_US_CA_STRAIGHT_TIME_FF connects with the schedule subroutine XX_UTIL_GET_EMPLOYMENT_SCHEDULE, caching the worker's exact weekly schedule limit as weekly_sch_hrs before processing the time card details.
2. End-to-End Worked Scenarios
📈 Scenario A: Schedule Met with Surplus (Straight Time Accrual)
Employee Assigned Schedule: 37.5 Hours. The employee logs 40 Hours of Regular Hours across the week.
📉 Scenario B: Schedule Shortfall (Unpaid Time Calculation)
Employee Assigned Schedule: 37.5 Hours (7.5 hrs/day). The worker logs 3 days (22.5 Hours) and records no authorized leave for the remaining days.
Result: The engine outputs 15.0 hours of unpaid time. When mapped in the Time Consumer Set to a reduction element, Global Payroll can automate salary docking without manual administrator recalculation.
3. Array Inputs & Outputs Specification
| Parameter Name | Classification | Behavior in Calculation |
|---|---|---|
| HWM_CTXARY_RECORD_POSITIONS | Context Array | Marks array record phases: HEADER, DETAIL, or END_PERIOD. |
| PayrollTimeType / AbsenceType | Input Arrays | Distinguishes worked time (Regular Hours) from approved leaves so absences fulfill schedule obligations. |
| OUT_MEASURE_UNDER | Output Array | Net regular hours per row that remain within the weekly schedule baseline. |
| OUT_MEASURE_STRAIGHT_TIME | Output Array | Surplus hours over the schedule written at END_PERIOD to the last regular hours row index. |
| OUT_MEASURE_UNPAID_TIME | Output Array | Deficit measure calculated when reported hours fall below schedule (weekly_sch_hrs − calc_reg_hours_wk). |
4. Formula Source Code
/* +======================================================================+
| Formula Name : OTL_US_CA_STRAIGHT_TIME_FF |
| Formula Type : Time Calculation rule |
| Jurisdiction : California (CA Alternative / Weekly Threshold) |
| Description : Calculates straight time and unpaid hours using |
| published weekly schedule as dynamic threshold. |
+======================================================================+ */
DEFAULT FOR HWM_CTXARY_RECORD_POSITIONS is EMPTY_TEXT_NUMBER
DEFAULT FOR HWM_CTXARY_HWM_MEASURE_DAY is EMPTY_NUMBER_NUMBER
DEFAULT FOR measure is EMPTY_NUMBER_NUMBER
DEFAULT FOR StartTime is EMPTY_DATE_NUMBER
DEFAULT FOR StopTime is EMPTY_DATE_NUMBER
DEFAULT FOR PayrollTimeType IS EMPTY_TEXT_NUMBER
DEFAULT FOR AbsenceType IS EMPTY_NUMBER_NUMBER
INPUTS ARE
HWM_CTXARY_RECORD_POSITIONS,
HWM_CTXARY_HWM_MEASURE_DAY,
measure,
StartTime,
StopTime,
PayrollTimeType,
AbsenceType
/* Required Context Hooks */
ffs_id = GET_CONTEXT(HWM_FFS_ID, 0)
rule_id = GET_CONTEXT(HWM_RULE_ID, 0)
ffName = 'OTL_US_CA_STRAIGHT_TIME_FF'
rLog = add_rlog(ffs_id, rule_id, '>>> Enter - ' || ffName)
/* Constant Setup */
NullDate = '01-JAN-1900'(DATE)
NullDateTime = '1900/01/01 00:00:00'(DATE)
NullText = '**FF_NULL**'
measure_period = GET_CONTEXT(HWM_MEASURE_PERIOD, 0)
/* Rule Header & Parameters */
hSumLvl = Get_Hdr_Text(rule_id, 'RUN_SUMMATION_LEVEL', 'TIMECARD')
hExecType = Get_Hdr_Text(rule_id, 'RULE_EXEC_TYPE', 'CREATE')
hCreateYn = 'N'
IF (upper(hExecType) = 'CREATE') THEN (
hCreateYn = 'Y'
)
pCategoryId = get_rvalue_number(rule_id, 'WORKED_TIME_CONDITION', 0)
pMaxHrs = get_rvalue_number(rule_id, 'DEFINED_LIMIT', 0)
/* Initialize Output Array Variables */
OUT_MEASURE_UNDER = EMPTY_NUMBER_NUMBER
OUT_MEASURE_STRAIGHT_TIME = EMPTY_NUMBER_NUMBER
OUT_MEASURE_UNPAID_TIME = EMPTY_NUMBER_NUMBER
/* Workarea Setup */
wMaAry = HWM_CTXARY_RECORD_POSITIONS.count
nidx = 0
st_time = 0
st_time_cur = 0
st_time_idx = 0
calc_reg_hours_wk = 0
weekly_sch_hrs = 0
/* Process Time Card Arrays */
WHILE (nidx < wMaAry) LOOP (
nidx = nidx + 1
tcMeasure = 0
tcMeasureDay = 0
aiPTT = NullText
aiAbs = 0
aiRecPosition = HWM_CTXARY_RECORD_POSITIONS[nidx]
IF (MEASURE.exists(nidx)) THEN ( tcMeasure = MEASURE[nidx] )
IF (HWM_CTXARY_HWM_MEASURE_DAY.exists(nidx)) THEN ( tcMeasureDay = HWM_CTXARY_HWM_MEASURE_DAY[nidx] )
IF (PayrollTimeType.EXISTS(nidx)) THEN ( aiPTT = PayrollTimeType[nidx] )
IF (AbsenceType.EXISTS(nidx)) THEN ( aiAbs = AbsenceType[nidx] )
/* 1. Header Evaluation: Fetch California Assigned Schedule */
IF (aiRecPosition = 'HEADER') THEN (
weekly_sch_hrs = 0
IF (STARTTIME.exists(nidx)) THEN ( aiStartTime = STARTTIME[nidx] )
IF (STOPTIME.exists(nidx)) THEN ( aiStopTime = STOPTIME[nidx] )
CALL_FORMULA('XX_UTIL_GET_EMPLOYMENT_SCHEDULE',
aiStartTime > 'start_date_override',
aiStopTime > 'end_date_override',
weekly_sch_hrs < 'TotalHrs' DEFAULT 0)
)
/* 2. Detail Evaluation: Accumulate Regular Hours */
ELSE IF (aiRecPosition = 'DETAIL' AND aiPTT = 'Regular Hours') THEN (
calc_reg_hours_wk = calc_reg_hours_wk + tcMeasure
IF (calc_reg_hours_wk > weekly_sch_hrs) THEN (
st_time_cur = calc_reg_hours_wk - weekly_sch_hrs
st_time = st_time + st_time_cur
calc_reg_hours_wk = weekly_sch_hrs
) ELSE (
st_time_cur = 0
)
OUT_MEASURE_UNDER[nidx] = tcMeasure - st_time_cur
st_time_idx = nidx
)
/* 3. Detail Evaluation: Accumulate Authorized Absences */
ELSE IF (aiRecPosition = 'DETAIL' AND aiAbs > 0) THEN (
calc_reg_hours_wk = calc_reg_hours_wk + tcMeasure
IF (calc_reg_hours_wk > weekly_sch_hrs) THEN (
st_time_cur = calc_reg_hours_wk - weekly_sch_hrs
st_time = st_time + st_time_cur
calc_reg_hours_wk = weekly_sch_hrs
) ELSE (
st_time_cur = 0
)
)
/* 4. Period Close: Reconcile Straight Time or Unpaid Deficit */
ELSE IF (aiRecPosition = 'END_PERIOD') THEN (
/* Case 1: Threshold reached and straight time surplus exists */
IF (calc_reg_hours_wk = weekly_sch_hrs AND st_time > 0) THEN (
OUT_MEASURE_STRAIGHT_TIME[st_time_idx] = st_time
)
/* Case 2: Reported hours short of schedule -> calculate unpaid gap */
ELSE IF (calc_reg_hours_wk < weekly_sch_hrs AND calc_reg_hours_wk > 0) THEN (
OUT_MEASURE_UNPAID_TIME[st_time_idx] = (weekly_sch_hrs - calc_reg_hours_wk)
)
)
/* Loop Guard */
IF (nidx > 1000) THEN (
ex = raise_error(ffs_id, rule_id, 'Formula ' || ffName || ' terminated: loop limit exceeded.')
)
)
rLog = add_rlog(ffs_id, rule_id, '<< Exit - ' || ffName)
RETURN OUT_MEASURE_UNDER, OUT_MEASURE_STRAIGHT_TIME, OUT_MEASURE_UNPAID_TIME
⚠️ California Architectural Considerations for ACE Reviews
- Index Binding: Both
OUT_MEASURE_STRAIGHT_TIMEandOUT_MEASURE_UNPAID_TIMEare written tost_time_idx(the last processedRegular Hoursindex). Confirm whether the payroll calculation card mapping expects variances on the final worked row or on a separate summary layout line. - Absence Interaction: Absences fulfill the schedule requirement (
calc_reg_hours_wk += tcMeasure). BecauseOUT_MEASURE_UNDERis not set for absence rows, native absence plan balances and costing remain managed within Absence Management without duplication. - Payroll Deduction Integration: When mapping
OUT_MEASURE_UNPAID_TIME, ensure the corresponding Time Consumer Set passes this value to a negative earnings or deduction element to automate salary reconciliation.