Case Study 2: Payroll Calculation System

Background

Heartland Manufacturing Group (HMG) operates three manufacturing plants in Ohio, employing a workforce of 1,450 hourly and salaried workers. Every two weeks, HMG's payroll department processes earnings for all employees, calculating gross pay, federal and state taxes, FICA contributions, voluntary deductions, and net pay. The payroll system runs as a COBOL batch program on HMG's IBM z/OS mainframe, processing the entire workforce in the Friday evening batch window.

In early 2024, HMG's payroll manager, Linda Yamamoto, identified a problem: the existing payroll program had been patched repeatedly over 18 years to accommodate new tax brackets, deduction types, and overtime rules. The accumulated patches had introduced inconsistencies in how rounding was applied -- some calculations used ROUNDED, others did not, and two subroutines used different rounding methods for the same type of calculation. During a routine internal audit, the auditor found that 23 employees had received paychecks that were off by one or two cents due to inconsistent rounding of tax withholding amounts.

Linda authorized a complete rewrite of the payroll calculation engine. The lead developer, Marcus Washington, designed the new system with three non-negotiable requirements:

  1. Every monetary calculation must use the ROUNDED phrase
  2. Every arithmetic operation must include ON SIZE ERROR protection
  3. Progressive tax bracket calculations must produce results that match the IRS withholding tables to the penny

This case study follows Marcus's implementation of the payroll calculation system, demonstrating COMPUTE for complex formulas, EVALUATE for tax bracket routing, ROUNDED for currency precision, and ON SIZE ERROR for overflow protection.

The Requirements

The payroll program must compute the following for each employee per pay period:

  1. Gross pay -- regular hours times hourly rate, plus overtime at 1.5x
  2. Federal tax withholding -- using progressive tax brackets for the employee's filing status
  3. State tax withholding -- Ohio's progressive income tax schedule
  4. FICA taxes -- Social Security (6.2%) and Medicare (1.45%), with the Social Security wage base cap
  5. Voluntary deductions -- health insurance, dental, 401(k), and union dues
  6. Net pay -- gross pay minus all deductions

The Complete Program

The following program demonstrates the payroll calculation engine processing a small payroll of three employees with different pay rates, hours, and filing statuses.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PAYROLL.
      *================================================================*
      * Program:     PAYROLL
      * Description: Biweekly Payroll Calculation System
      * Author:      Marcus Washington
      * Date:        2024-02-15
      * Purpose:     Demonstrates COBOL arithmetic operations for
      *              payroll processing at Heartland Manufacturing.
      *              Uses COMPUTE, ROUNDED, ON SIZE ERROR, and
      *              EVALUATE for progressive tax bracket calculations.
      *================================================================*

       DATA DIVISION.
       WORKING-STORAGE SECTION.

      *================================================================*
      * PROGRAM CONSTANTS                                              *
      *================================================================*
       01  WS-C-PROGRAM-INFO.
           05  WS-C-PROGRAM-ID       PIC X(08) VALUE 'PAYROLL '.
           05  WS-C-PAY-PERIOD-TYPE  PIC X(10) VALUE 'BI-WEEKLY '.
           05  WS-C-PAY-PERIODS-YR   PIC 9(02) VALUE 26.

      *--- Hours and overtime constants ---
       01  WS-C-HOURS-CONSTANTS.
           05  WS-C-MAX-REG-HOURS    PIC 9(02)V9 VALUE 80.0.
           05  WS-C-OVERTIME-MULT    PIC 9(01)V9 VALUE 1.5.
           05  WS-C-MAX-HOURS-PERIOD PIC 9(03)V9 VALUE 120.0.

      *--- FICA constants (2024 rates) ---
       01  WS-C-FICA-CONSTANTS.
           05  WS-C-SS-RATE          PIC V9(04) VALUE 0.0620.
           05  WS-C-SS-WAGE-BASE     PIC 9(09)V99
               VALUE 168600.00.
           05  WS-C-MEDICARE-RATE    PIC V9(04) VALUE 0.0145.
           05  WS-C-MEDICARE-SURTAX  PIC V9(04) VALUE 0.0090.
           05  WS-C-SURTAX-THRESH    PIC 9(09)V99
               VALUE 200000.00.

      *--- Federal tax brackets (single filer, biweekly, 2024) ---
      *    Brackets are stored as annualized amounts for clarity
       01  WS-C-FED-BRACKETS.
           05  WS-C-FED-BRKT-1-MAX   PIC 9(07)V99
               VALUE 11600.00.
           05  WS-C-FED-BRKT-1-RATE  PIC V9(04) VALUE 0.1000.
           05  WS-C-FED-BRKT-2-MAX   PIC 9(07)V99
               VALUE 47150.00.
           05  WS-C-FED-BRKT-2-RATE  PIC V9(04) VALUE 0.1200.
           05  WS-C-FED-BRKT-3-MAX   PIC 9(07)V99
               VALUE 100525.00.
           05  WS-C-FED-BRKT-3-RATE  PIC V9(04) VALUE 0.2200.
           05  WS-C-FED-BRKT-4-MAX   PIC 9(07)V99
               VALUE 191950.00.
           05  WS-C-FED-BRKT-4-RATE  PIC V9(04) VALUE 0.2400.
           05  WS-C-FED-BRKT-5-RATE  PIC V9(04) VALUE 0.3200.

      *--- Ohio state tax brackets (2024 simplified) ---
       01  WS-C-STATE-BRACKETS.
           05  WS-C-ST-BRKT-1-MAX    PIC 9(07)V99
               VALUE 26050.00.
           05  WS-C-ST-BRKT-1-RATE   PIC V9(04) VALUE 0.0000.
           05  WS-C-ST-BRKT-2-MAX    PIC 9(07)V99
               VALUE 46100.00.
           05  WS-C-ST-BRKT-2-RATE   PIC V9(04) VALUE 0.0275.
           05  WS-C-ST-BRKT-3-MAX    PIC 9(07)V99
               VALUE 92150.00.
           05  WS-C-ST-BRKT-3-RATE   PIC V9(04) VALUE 0.0325.
           05  WS-C-ST-BRKT-4-RATE   PIC V9(04) VALUE 0.0375.

      *--- Deduction constants ---
       01  WS-C-DEDUCTION-RATES.
           05  WS-C-HEALTH-SINGLE    PIC 9(05)V99
               VALUE 00185.50.
           05  WS-C-HEALTH-FAMILY    PIC 9(05)V99
               VALUE 00425.00.
           05  WS-C-DENTAL-SINGLE    PIC 9(05)V99
               VALUE 00028.75.
           05  WS-C-DENTAL-FAMILY    PIC 9(05)V99
               VALUE 00072.50.
           05  WS-C-UNION-DUES       PIC 9(05)V99
               VALUE 00045.00.

      *--- Report constants ---
       01  WS-C-REPORT-SEP           PIC X(65) VALUE ALL '='.
       01  WS-C-DETAIL-SEP           PIC X(65) VALUE ALL '-'.

      *================================================================*
      * FLAGS AND SWITCHES                                             *
      *================================================================*
       01  WS-F-OVERFLOW-FLAG        PIC X(01) VALUE 'N'.
           88  WS-OVERFLOW-OCCURRED              VALUE 'Y'.
           88  WS-NO-OVERFLOW                    VALUE 'N'.

       01  WS-F-SS-CAPPED-FLAG      PIC X(01) VALUE 'N'.
           88  WS-SS-WAGE-CAPPED                 VALUE 'Y'.
           88  WS-SS-WAGE-NOT-CAPPED             VALUE 'N'.

      *================================================================*
      * COUNTERS AND ACCUMULATORS                                      *
      *================================================================*
       01  WS-CTR-EMPLOYEES         PIC 9(05)  VALUE ZERO.

       01  WS-ACC-PAYROLL-TOTALS.
           05  WS-ACC-GROSS-PAY     PIC 9(11)V99 VALUE ZERO.
           05  WS-ACC-FED-TAX       PIC 9(09)V99 VALUE ZERO.
           05  WS-ACC-STATE-TAX     PIC 9(09)V99 VALUE ZERO.
           05  WS-ACC-SS-TAX        PIC 9(09)V99 VALUE ZERO.
           05  WS-ACC-MEDICARE-TAX  PIC 9(09)V99 VALUE ZERO.
           05  WS-ACC-DEDUCTIONS    PIC 9(09)V99 VALUE ZERO.
           05  WS-ACC-NET-PAY       PIC 9(11)V99 VALUE ZERO.

      *================================================================*
      * EMPLOYEE INPUT RECORD                                          *
      *================================================================*
       01  WS-EMPLOYEE-RECORD.
           05  WS-EMP-ID            PIC X(06).
           05  WS-EMP-NAME          PIC X(25).
           05  WS-EMP-PAY-TYPE      PIC X(01).
               88  WS-EMP-HOURLY               VALUE 'H'.
               88  WS-EMP-SALARIED             VALUE 'S'.
           05  WS-EMP-HOURLY-RATE   PIC 9(04)V99.
           05  WS-EMP-SALARY-ANNUAL PIC 9(07)V99.
           05  WS-EMP-HOURS-WORKED  PIC 9(03)V9.
           05  WS-EMP-FILING-STATUS PIC X(01).
               88  WS-FILING-SINGLE            VALUE 'S'.
               88  WS-FILING-MARRIED           VALUE 'M'.
               88  WS-FILING-HEAD-HH           VALUE 'H'.
           05  WS-EMP-FED-ALLOWANCES PIC 9(02).
           05  WS-EMP-401K-PCT      PIC 9(02)V99.
           05  WS-EMP-HEALTH-PLAN   PIC X(01).
               88  WS-HEALTH-NONE              VALUE 'N'.
               88  WS-HEALTH-SINGLE            VALUE 'S'.
               88  WS-HEALTH-FAMILY            VALUE 'F'.
           05  WS-EMP-DENTAL-PLAN   PIC X(01).
               88  WS-DENTAL-NONE              VALUE 'N'.
               88  WS-DENTAL-SINGLE            VALUE 'S'.
               88  WS-DENTAL-FAMILY            VALUE 'F'.
           05  WS-EMP-UNION-MEMBER  PIC X(01).
               88  WS-IS-UNION-MEMBER          VALUE 'Y'.
               88  WS-NOT-UNION-MEMBER         VALUE 'N'.
           05  WS-EMP-YTD-GROSS     PIC 9(09)V99.
           05  WS-EMP-YTD-SS-WAGES  PIC 9(09)V99.

      *================================================================*
      * CALCULATION WORK FIELDS                                        *
      *================================================================*
       01  WS-WK-PAY-FIELDS.
           05  WS-WK-REG-HOURS      PIC 9(03)V9  VALUE ZERO.
           05  WS-WK-OT-HOURS       PIC 9(03)V9  VALUE ZERO.
           05  WS-WK-REG-PAY        PIC 9(07)V99 VALUE ZERO.
           05  WS-WK-OT-PAY         PIC 9(07)V99 VALUE ZERO.
           05  WS-WK-GROSS-PAY      PIC 9(07)V99 VALUE ZERO.

       01  WS-WK-TAX-FIELDS.
           05  WS-WK-ANNUALIZED     PIC 9(09)V99 VALUE ZERO.
           05  WS-WK-TAXABLE-INCOME PIC 9(09)V99 VALUE ZERO.
           05  WS-WK-REMAINING-INC  PIC 9(09)V99 VALUE ZERO.
           05  WS-WK-BRACKET-TAX    PIC 9(07)V99 VALUE ZERO.
           05  WS-WK-ANNUAL-FED-TAX PIC 9(07)V99 VALUE ZERO.
           05  WS-WK-FED-TAX        PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-ANNUAL-ST-TAX  PIC 9(07)V99 VALUE ZERO.
           05  WS-WK-STATE-TAX      PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-SS-TAXABLE     PIC 9(09)V99 VALUE ZERO.
           05  WS-WK-SS-TAX         PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-MEDICARE-TAX   PIC 9(05)V99 VALUE ZERO.

       01  WS-WK-DEDUCTION-FIELDS.
           05  WS-WK-HEALTH-DED     PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-DENTAL-DED     PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-401K-DED       PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-UNION-DED      PIC 9(05)V99 VALUE ZERO.
           05  WS-WK-TOTAL-DED      PIC 9(07)V99 VALUE ZERO.

       01  WS-WK-NET-PAY            PIC S9(07)V99 VALUE ZERO.
       01  WS-WK-TOTAL-TAXES        PIC 9(07)V99 VALUE ZERO.

      *================================================================*
      * DISPLAY FIELDS                                                 *
      *================================================================*
       01  WS-DSP-AMOUNT            PIC $$$,$$$,MATH1$9.99.
       01  WS-DSP-PCT               PIC Z9.99.
       01  WS-DSP-COUNT             PIC Z,ZZ9.

       PROCEDURE DIVISION.
       0000-MAIN-CONTROL.
           PERFORM 1000-INITIALIZATION
           PERFORM 2000-PROCESS-PAYROLL
           PERFORM 3000-PRINT-TOTALS
           STOP RUN
           .

      *================================================================*
      * 1000-INITIALIZATION                                            *
      *================================================================*
       1000-INITIALIZATION.
           DISPLAY WS-C-REPORT-SEP
           DISPLAY '  HEARTLAND MANUFACTURING GROUP'
           DISPLAY '  BIWEEKLY PAYROLL REGISTER'
           DISPLAY WS-C-REPORT-SEP
           DISPLAY SPACES
           .

      *================================================================*
      * 2000-PROCESS-PAYROLL: Process all employees                    *
      *================================================================*
       2000-PROCESS-PAYROLL.
      *    Employee 1: Hourly, overtime, single filer
           PERFORM 2050-CLEAR-EMPLOYEE
           PERFORM 2100-LOAD-EMPLOYEE-1
           PERFORM 2200-CALCULATE-GROSS-PAY
           PERFORM 2300-CALCULATE-FEDERAL-TAX
           PERFORM 2400-CALCULATE-STATE-TAX
           PERFORM 2500-CALCULATE-FICA
           PERFORM 2600-CALCULATE-DEDUCTIONS
           PERFORM 2700-CALCULATE-NET-PAY
           PERFORM 2800-ACCUMULATE-TOTALS
           PERFORM 2900-DISPLAY-PAY-STUB

      *    Employee 2: Salaried, married filer
           PERFORM 2050-CLEAR-EMPLOYEE
           PERFORM 2100-LOAD-EMPLOYEE-2
           PERFORM 2200-CALCULATE-GROSS-PAY
           PERFORM 2300-CALCULATE-FEDERAL-TAX
           PERFORM 2400-CALCULATE-STATE-TAX
           PERFORM 2500-CALCULATE-FICA
           PERFORM 2600-CALCULATE-DEDUCTIONS
           PERFORM 2700-CALCULATE-NET-PAY
           PERFORM 2800-ACCUMULATE-TOTALS
           PERFORM 2900-DISPLAY-PAY-STUB

      *    Employee 3: Hourly, no overtime, head of household
           PERFORM 2050-CLEAR-EMPLOYEE
           PERFORM 2100-LOAD-EMPLOYEE-3
           PERFORM 2200-CALCULATE-GROSS-PAY
           PERFORM 2300-CALCULATE-FEDERAL-TAX
           PERFORM 2400-CALCULATE-STATE-TAX
           PERFORM 2500-CALCULATE-FICA
           PERFORM 2600-CALCULATE-DEDUCTIONS
           PERFORM 2700-CALCULATE-NET-PAY
           PERFORM 2800-ACCUMULATE-TOTALS
           PERFORM 2900-DISPLAY-PAY-STUB
           .

      *================================================================*
      * 2050-CLEAR-EMPLOYEE: Reset per-employee fields                 *
      *================================================================*
       2050-CLEAR-EMPLOYEE.
           INITIALIZE WS-WK-PAY-FIELDS
           INITIALIZE WS-WK-TAX-FIELDS
           INITIALIZE WS-WK-DEDUCTION-FIELDS
           MOVE ZERO TO WS-WK-NET-PAY
           MOVE ZERO TO WS-WK-TOTAL-TAXES
           SET WS-NO-OVERFLOW TO TRUE
           SET WS-SS-WAGE-NOT-CAPPED TO TRUE
           .

      *================================================================*
      * 2100-LOAD-EMPLOYEE-1: Hourly worker with overtime              *
      *================================================================*
       2100-LOAD-EMPLOYEE-1.
           MOVE 'E10042' TO WS-EMP-ID
           MOVE 'JOHNSON, ROBERT A.'  TO WS-EMP-NAME
           SET  WS-EMP-HOURLY    TO TRUE
           MOVE 0024.50          TO WS-EMP-HOURLY-RATE
           MOVE ZERO             TO WS-EMP-SALARY-ANNUAL
           MOVE 092.5            TO WS-EMP-HOURS-WORKED
           SET  WS-FILING-SINGLE TO TRUE
           MOVE 01               TO WS-EMP-FED-ALLOWANCES
           MOVE 06.00            TO WS-EMP-401K-PCT
           SET  WS-HEALTH-SINGLE TO TRUE
           SET  WS-DENTAL-SINGLE TO TRUE
           SET  WS-IS-UNION-MEMBER TO TRUE
           MOVE 038220.00        TO WS-EMP-YTD-GROSS
           MOVE 038220.00        TO WS-EMP-YTD-SS-WAGES
           .

      *================================================================*
      * Load Employee 2: Salaried, married filer                       *
      *================================================================*
       2100-LOAD-EMPLOYEE-2.
           MOVE 'E20015' TO WS-EMP-ID
           MOVE 'CHEN, VICTORIA M.'   TO WS-EMP-NAME
           SET  WS-EMP-SALARIED  TO TRUE
           MOVE ZERO             TO WS-EMP-HOURLY-RATE
           MOVE 078000.00        TO WS-EMP-SALARY-ANNUAL
           MOVE 080.0            TO WS-EMP-HOURS-WORKED
           SET  WS-FILING-MARRIED TO TRUE
           MOVE 03               TO WS-EMP-FED-ALLOWANCES
           MOVE 08.00            TO WS-EMP-401K-PCT
           SET  WS-HEALTH-FAMILY TO TRUE
           SET  WS-DENTAL-FAMILY TO TRUE
           SET  WS-NOT-UNION-MEMBER TO TRUE
           MOVE 060000.00        TO WS-EMP-YTD-GROSS
           MOVE 060000.00        TO WS-EMP-YTD-SS-WAGES
           .

      *================================================================*
      * Load Employee 3: Hourly, no overtime, head of household        *
      *================================================================*
       2100-LOAD-EMPLOYEE-3.
           MOVE 'E10087' TO WS-EMP-ID
           MOVE 'WILLIAMS, MARIA T.'  TO WS-EMP-NAME
           SET  WS-EMP-HOURLY    TO TRUE
           MOVE 0019.75          TO WS-EMP-HOURLY-RATE
           MOVE ZERO             TO WS-EMP-SALARY-ANNUAL
           MOVE 076.0            TO WS-EMP-HOURS-WORKED
           SET  WS-FILING-HEAD-HH TO TRUE
           MOVE 02               TO WS-EMP-FED-ALLOWANCES
           MOVE 03.00            TO WS-EMP-401K-PCT
           SET  WS-HEALTH-FAMILY TO TRUE
           SET  WS-DENTAL-NONE   TO TRUE
           SET  WS-IS-UNION-MEMBER TO TRUE
           MOVE 024675.00        TO WS-EMP-YTD-GROSS
           MOVE 024675.00        TO WS-EMP-YTD-SS-WAGES
           .

      *================================================================*
      * 2200-CALCULATE-GROSS-PAY                                       *
      * Computes regular pay and overtime pay.                         *
      * Overtime applies after 80 hours (biweekly period).             *
      *================================================================*
       2200-CALCULATE-GROSS-PAY.
           EVALUATE TRUE
               WHEN WS-EMP-HOURLY
      *            Determine regular and overtime hours
                   IF WS-EMP-HOURS-WORKED > WS-C-MAX-REG-HOURS
                       MOVE WS-C-MAX-REG-HOURS
                           TO WS-WK-REG-HOURS
                       COMPUTE WS-WK-OT-HOURS =
                           WS-EMP-HOURS-WORKED
                           - WS-C-MAX-REG-HOURS
                   ELSE
                       MOVE WS-EMP-HOURS-WORKED
                           TO WS-WK-REG-HOURS
                       MOVE ZERO TO WS-WK-OT-HOURS
                   END-IF

      *            Calculate regular pay
                   COMPUTE WS-WK-REG-PAY ROUNDED =
                       WS-WK-REG-HOURS * WS-EMP-HOURLY-RATE
                       ON SIZE ERROR
                           SET WS-OVERFLOW-OCCURRED TO TRUE
                           DISPLAY 'OVERFLOW: Regular pay calc'
                   END-COMPUTE

      *            Calculate overtime pay at 1.5x rate
                   IF WS-WK-OT-HOURS > ZERO
                       COMPUTE WS-WK-OT-PAY ROUNDED =
                           WS-WK-OT-HOURS
                           * WS-EMP-HOURLY-RATE
                           * WS-C-OVERTIME-MULT
                           ON SIZE ERROR
                               SET WS-OVERFLOW-OCCURRED TO TRUE
                               DISPLAY 'OVERFLOW: OT pay calc'
                       END-COMPUTE
                   END-IF

      *            Total gross pay
                   ADD WS-WK-REG-PAY WS-WK-OT-PAY
                       GIVING WS-WK-GROSS-PAY
                       ON SIZE ERROR
                           SET WS-OVERFLOW-OCCURRED TO TRUE
                           DISPLAY 'OVERFLOW: Gross pay calc'
                   END-ADD

               WHEN WS-EMP-SALARIED
      *            Biweekly salary = annual / 26
                   COMPUTE WS-WK-GROSS-PAY ROUNDED =
                       WS-EMP-SALARY-ANNUAL
                       / WS-C-PAY-PERIODS-YR
                       ON SIZE ERROR
                           SET WS-OVERFLOW-OCCURRED TO TRUE
                           DISPLAY 'OVERFLOW: Salary calc'
                   END-COMPUTE
                   MOVE WS-WK-GROSS-PAY TO WS-WK-REG-PAY
                   MOVE ZERO TO WS-WK-OT-PAY
           END-EVALUATE
           .

      *================================================================*
      * 2300-CALCULATE-FEDERAL-TAX                                     *
      * Progressive tax bracket calculation.                           *
      * Strategy: Annualize the gross, calculate annual tax,           *
      *           then divide by 26 for the per-period amount.         *
      *================================================================*
       2300-CALCULATE-FEDERAL-TAX.
      *    Annualize the gross pay
           COMPUTE WS-WK-ANNUALIZED ROUNDED =
               WS-WK-GROSS-PAY * WS-C-PAY-PERIODS-YR
               ON SIZE ERROR
                   SET WS-OVERFLOW-OCCURRED TO TRUE
           END-COMPUTE

           MOVE WS-WK-ANNUALIZED TO WS-WK-REMAINING-INC
           MOVE ZERO TO WS-WK-ANNUAL-FED-TAX

      *    Bracket 1: 10% on first $11,600
           IF WS-WK-REMAINING-INC > ZERO
               IF WS-WK-REMAINING-INC > WS-C-FED-BRKT-1-MAX
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-C-FED-BRKT-1-MAX
                       * WS-C-FED-BRKT-1-RATE
                   SUBTRACT WS-C-FED-BRKT-1-MAX
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-FED-BRKT-1-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-FED-TAX
           END-IF

      *    Bracket 2: 12% on $11,601 to $47,150
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-TAXABLE-INCOME =
                   WS-C-FED-BRKT-2-MAX - WS-C-FED-BRKT-1-MAX
               IF WS-WK-REMAINING-INC > WS-WK-TAXABLE-INCOME
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-TAXABLE-INCOME
                       * WS-C-FED-BRKT-2-RATE
                   SUBTRACT WS-WK-TAXABLE-INCOME
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-FED-BRKT-2-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-FED-TAX
           END-IF

      *    Bracket 3: 22% on $47,151 to $100,525
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-TAXABLE-INCOME =
                   WS-C-FED-BRKT-3-MAX - WS-C-FED-BRKT-2-MAX
               IF WS-WK-REMAINING-INC > WS-WK-TAXABLE-INCOME
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-TAXABLE-INCOME
                       * WS-C-FED-BRKT-3-RATE
                   SUBTRACT WS-WK-TAXABLE-INCOME
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-FED-BRKT-3-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-FED-TAX
           END-IF

      *    Bracket 4: 24% on $100,526 to $191,950
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-TAXABLE-INCOME =
                   WS-C-FED-BRKT-4-MAX - WS-C-FED-BRKT-3-MAX
               IF WS-WK-REMAINING-INC > WS-WK-TAXABLE-INCOME
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-TAXABLE-INCOME
                       * WS-C-FED-BRKT-4-RATE
                   SUBTRACT WS-WK-TAXABLE-INCOME
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-FED-BRKT-4-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-FED-TAX
           END-IF

      *    Bracket 5: 32% on everything above $191,950
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                   WS-WK-REMAINING-INC
                   * WS-C-FED-BRKT-5-RATE
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-FED-TAX
               MOVE ZERO TO WS-WK-REMAINING-INC
           END-IF

      *    Convert annual tax to per-period amount
           IF WS-C-PAY-PERIODS-YR > ZERO
               COMPUTE WS-WK-FED-TAX ROUNDED =
                   WS-WK-ANNUAL-FED-TAX / WS-C-PAY-PERIODS-YR
                   ON SIZE ERROR
                       SET WS-OVERFLOW-OCCURRED TO TRUE
                       DISPLAY 'OVERFLOW: Federal tax calc'
               END-COMPUTE
           ELSE
               MOVE ZERO TO WS-WK-FED-TAX
           END-IF
           .

      *================================================================*
      * 2400-CALCULATE-STATE-TAX                                       *
      * Ohio progressive income tax with simplified brackets.          *
      *================================================================*
       2400-CALCULATE-STATE-TAX.
           MOVE WS-WK-ANNUALIZED TO WS-WK-REMAINING-INC
           MOVE ZERO TO WS-WK-ANNUAL-ST-TAX

      *    Bracket 1: 0% on first $26,050
           IF WS-WK-REMAINING-INC > WS-C-ST-BRKT-1-MAX
               SUBTRACT WS-C-ST-BRKT-1-MAX
                   FROM WS-WK-REMAINING-INC
           ELSE
               MOVE ZERO TO WS-WK-REMAINING-INC
           END-IF

      *    Bracket 2: 2.75% on $26,051 to $46,100
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-TAXABLE-INCOME =
                   WS-C-ST-BRKT-2-MAX - WS-C-ST-BRKT-1-MAX
               IF WS-WK-REMAINING-INC > WS-WK-TAXABLE-INCOME
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-TAXABLE-INCOME
                       * WS-C-ST-BRKT-2-RATE
                   SUBTRACT WS-WK-TAXABLE-INCOME
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-ST-BRKT-2-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-ST-TAX
           END-IF

      *    Bracket 3: 3.25% on $46,101 to $92,150
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-TAXABLE-INCOME =
                   WS-C-ST-BRKT-3-MAX - WS-C-ST-BRKT-2-MAX
               IF WS-WK-REMAINING-INC > WS-WK-TAXABLE-INCOME
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-TAXABLE-INCOME
                       * WS-C-ST-BRKT-3-RATE
                   SUBTRACT WS-WK-TAXABLE-INCOME
                       FROM WS-WK-REMAINING-INC
               ELSE
                   COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                       WS-WK-REMAINING-INC
                       * WS-C-ST-BRKT-3-RATE
                   MOVE ZERO TO WS-WK-REMAINING-INC
               END-IF
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-ST-TAX
           END-IF

      *    Bracket 4: 3.75% on everything above $92,150
           IF WS-WK-REMAINING-INC > ZERO
               COMPUTE WS-WK-BRACKET-TAX ROUNDED =
                   WS-WK-REMAINING-INC
                   * WS-C-ST-BRKT-4-RATE
               ADD WS-WK-BRACKET-TAX TO WS-WK-ANNUAL-ST-TAX
           END-IF

      *    Convert annual state tax to per-period amount
           IF WS-C-PAY-PERIODS-YR > ZERO
               COMPUTE WS-WK-STATE-TAX ROUNDED =
                   WS-WK-ANNUAL-ST-TAX / WS-C-PAY-PERIODS-YR
                   ON SIZE ERROR
                       SET WS-OVERFLOW-OCCURRED TO TRUE
                       DISPLAY 'OVERFLOW: State tax calc'
               END-COMPUTE
           ELSE
               MOVE ZERO TO WS-WK-STATE-TAX
           END-IF
           .

      *================================================================*
      * 2500-CALCULATE-FICA                                            *
      * Social Security (6.2% up to wage base) and Medicare (1.45%).   *
      *================================================================*
       2500-CALCULATE-FICA.
      *    Social Security: check if wage base has been reached
           COMPUTE WS-WK-SS-TAXABLE =
               WS-C-SS-WAGE-BASE - WS-EMP-YTD-SS-WAGES

           IF WS-WK-SS-TAXABLE <= ZERO
      *        Already over the wage base, no SS tax this period
               MOVE ZERO TO WS-WK-SS-TAX
               SET WS-SS-WAGE-CAPPED TO TRUE
           ELSE
               IF WS-WK-SS-TAXABLE < WS-WK-GROSS-PAY
      *            Will hit the cap this period
                   COMPUTE WS-WK-SS-TAX ROUNDED =
                       WS-WK-SS-TAXABLE * WS-C-SS-RATE
                       ON SIZE ERROR
                           SET WS-OVERFLOW-OCCURRED TO TRUE
                   END-COMPUTE
                   SET WS-SS-WAGE-CAPPED TO TRUE
               ELSE
      *            Full gross is taxable for SS
                   COMPUTE WS-WK-SS-TAX ROUNDED =
                       WS-WK-GROSS-PAY * WS-C-SS-RATE
                       ON SIZE ERROR
                           SET WS-OVERFLOW-OCCURRED TO TRUE
                   END-COMPUTE
               END-IF
           END-IF

      *    Medicare: no wage base limit (1.45% on all wages)
           COMPUTE WS-WK-MEDICARE-TAX ROUNDED =
               WS-WK-GROSS-PAY * WS-C-MEDICARE-RATE
               ON SIZE ERROR
                   SET WS-OVERFLOW-OCCURRED TO TRUE
                   DISPLAY 'OVERFLOW: Medicare tax calc'
           END-COMPUTE
           .

      *================================================================*
      * 2600-CALCULATE-DEDUCTIONS                                      *
      * Health insurance, dental, 401(k), and union dues.              *
      *================================================================*
       2600-CALCULATE-DEDUCTIONS.
      *    Health insurance deduction
           EVALUATE TRUE
               WHEN WS-HEALTH-NONE
                   MOVE ZERO TO WS-WK-HEALTH-DED
               WHEN WS-HEALTH-SINGLE
                   MOVE WS-C-HEALTH-SINGLE TO WS-WK-HEALTH-DED
               WHEN WS-HEALTH-FAMILY
                   MOVE WS-C-HEALTH-FAMILY TO WS-WK-HEALTH-DED
           END-EVALUATE

      *    Dental insurance deduction
           EVALUATE TRUE
               WHEN WS-DENTAL-NONE
                   MOVE ZERO TO WS-WK-DENTAL-DED
               WHEN WS-DENTAL-SINGLE
                   MOVE WS-C-DENTAL-SINGLE TO WS-WK-DENTAL-DED
               WHEN WS-DENTAL-FAMILY
                   MOVE WS-C-DENTAL-FAMILY TO WS-WK-DENTAL-DED
           END-EVALUATE

      *    401(k) contribution (percentage of gross pay)
           IF WS-EMP-401K-PCT > ZERO
               COMPUTE WS-WK-401K-DED ROUNDED =
                   WS-WK-GROSS-PAY
                   * (WS-EMP-401K-PCT / 100)
                   ON SIZE ERROR
                       SET WS-OVERFLOW-OCCURRED TO TRUE
                       DISPLAY 'OVERFLOW: 401k calc'
               END-COMPUTE
           ELSE
               MOVE ZERO TO WS-WK-401K-DED
           END-IF

      *    Union dues
           IF WS-IS-UNION-MEMBER
               MOVE WS-C-UNION-DUES TO WS-WK-UNION-DED
           ELSE
               MOVE ZERO TO WS-WK-UNION-DED
           END-IF

      *    Total deductions (taxes + voluntary)
           COMPUTE WS-WK-TOTAL-TAXES =
               WS-WK-FED-TAX
               + WS-WK-STATE-TAX
               + WS-WK-SS-TAX
               + WS-WK-MEDICARE-TAX

           COMPUTE WS-WK-TOTAL-DED =
               WS-WK-HEALTH-DED
               + WS-WK-DENTAL-DED
               + WS-WK-401K-DED
               + WS-WK-UNION-DED
           .

      *================================================================*
      * 2700-CALCULATE-NET-PAY                                         *
      * Gross minus all taxes and deductions.                          *
      *================================================================*
       2700-CALCULATE-NET-PAY.
           COMPUTE WS-WK-NET-PAY ROUNDED =
               WS-WK-GROSS-PAY
               - WS-WK-TOTAL-TAXES
               - WS-WK-TOTAL-DED
               ON SIZE ERROR
                   SET WS-OVERFLOW-OCCURRED TO TRUE
                   DISPLAY 'OVERFLOW: Net pay calculation'
           END-COMPUTE

           IF WS-WK-NET-PAY < ZERO
               DISPLAY '*** WARNING: Negative net pay for '
                   WS-EMP-ID ' ***'
           END-IF
           .

      *================================================================*
      * 2800-ACCUMULATE-TOTALS: Add to payroll register totals         *
      *================================================================*
       2800-ACCUMULATE-TOTALS.
           ADD 1 TO WS-CTR-EMPLOYEES
           ADD WS-WK-GROSS-PAY   TO WS-ACC-GROSS-PAY
           ADD WS-WK-FED-TAX     TO WS-ACC-FED-TAX
           ADD WS-WK-STATE-TAX   TO WS-ACC-STATE-TAX
           ADD WS-WK-SS-TAX      TO WS-ACC-SS-TAX
           ADD WS-WK-MEDICARE-TAX TO WS-ACC-MEDICARE-TAX
           ADD WS-WK-TOTAL-DED   TO WS-ACC-DEDUCTIONS
           ADD WS-WK-NET-PAY     TO WS-ACC-NET-PAY
           .

      *================================================================*
      * 2900-DISPLAY-PAY-STUB: Format and display employee pay stub    *
      *================================================================*
       2900-DISPLAY-PAY-STUB.
           DISPLAY WS-C-DETAIL-SEP
           DISPLAY '  Employee: ' WS-EMP-ID '  '
               WS-EMP-NAME

           IF WS-EMP-HOURLY
               MOVE WS-EMP-HOURLY-RATE TO WS-DSP-RATE
               DISPLAY '  Pay Type: Hourly @ ' WS-DSP-RATE
                   '/hr'
               MOVE WS-WK-REG-HOURS TO WS-DSP-HOURS
               DISPLAY '  Regular Hours:   ' WS-DSP-HOURS
               MOVE WS-WK-OT-HOURS TO WS-DSP-HOURS
               DISPLAY '  Overtime Hours:  ' WS-DSP-HOURS
           ELSE
               MOVE WS-EMP-SALARY-ANNUAL TO WS-DSP-AMOUNT
               DISPLAY '  Pay Type: Salaried @ '
                   WS-DSP-AMOUNT '/yr'
           END-IF

           DISPLAY SPACES
           DISPLAY '  EARNINGS:'
           MOVE WS-WK-REG-PAY TO WS-DSP-AMOUNT
           DISPLAY '    Regular Pay:       ' WS-DSP-AMOUNT
           MOVE WS-WK-OT-PAY TO WS-DSP-AMOUNT
           DISPLAY '    Overtime Pay:      ' WS-DSP-AMOUNT
           MOVE WS-WK-GROSS-PAY TO WS-DSP-AMOUNT
           DISPLAY '    GROSS PAY:         ' WS-DSP-AMOUNT

           DISPLAY SPACES
           DISPLAY '  TAXES:'
           MOVE WS-WK-FED-TAX TO WS-DSP-AMOUNT
           DISPLAY '    Federal Tax:       ' WS-DSP-AMOUNT
           MOVE WS-WK-STATE-TAX TO WS-DSP-AMOUNT
           DISPLAY '    State Tax (OH):    ' WS-DSP-AMOUNT
           MOVE WS-WK-SS-TAX TO WS-DSP-AMOUNT
           DISPLAY '    Social Security:   ' WS-DSP-AMOUNT
           IF WS-SS-WAGE-CAPPED
               DISPLAY '      (SS wage base reached)'
           END-IF
           MOVE WS-WK-MEDICARE-TAX TO WS-DSP-AMOUNT
           DISPLAY '    Medicare:          ' WS-DSP-AMOUNT
           MOVE WS-WK-TOTAL-TAXES TO WS-DSP-AMOUNT
           DISPLAY '    TOTAL TAXES:       ' WS-DSP-AMOUNT

           DISPLAY SPACES
           DISPLAY '  DEDUCTIONS:'
           MOVE WS-WK-HEALTH-DED TO WS-DSP-AMOUNT
           DISPLAY '    Health Insurance:  ' WS-DSP-AMOUNT
           MOVE WS-WK-DENTAL-DED TO WS-DSP-AMOUNT
           DISPLAY '    Dental Insurance:  ' WS-DSP-AMOUNT
           MOVE WS-WK-401K-DED TO WS-DSP-AMOUNT
           DISPLAY '    401(k):            ' WS-DSP-AMOUNT
           MOVE WS-WK-UNION-DED TO WS-DSP-AMOUNT
           DISPLAY '    Union Dues:        ' WS-DSP-AMOUNT
           MOVE WS-WK-TOTAL-DED TO WS-DSP-AMOUNT
           DISPLAY '    TOTAL DEDUCTIONS:  ' WS-DSP-AMOUNT

           DISPLAY SPACES
           MOVE WS-WK-NET-PAY TO WS-DSP-AMOUNT
           DISPLAY '  *** NET PAY:         ' WS-DSP-AMOUNT
               ' ***'
           DISPLAY SPACES
           .

      *================================================================*
      * 3000-PRINT-TOTALS: Payroll register summary                    *
      *================================================================*
       3000-PRINT-TOTALS.
           DISPLAY WS-C-REPORT-SEP
           DISPLAY '  PAYROLL REGISTER SUMMARY'
           DISPLAY WS-C-REPORT-SEP

           MOVE WS-CTR-EMPLOYEES TO WS-DSP-COUNT
           DISPLAY '  Employees Processed: ' WS-DSP-COUNT
           DISPLAY SPACES

           MOVE WS-ACC-GROSS-PAY TO WS-DSP-AMOUNT
           DISPLAY '  Total Gross Pay:     ' WS-DSP-AMOUNT
           MOVE WS-ACC-FED-TAX TO WS-DSP-AMOUNT
           DISPLAY '  Total Federal Tax:   ' WS-DSP-AMOUNT
           MOVE WS-ACC-STATE-TAX TO WS-DSP-AMOUNT
           DISPLAY '  Total State Tax:     ' WS-DSP-AMOUNT
           MOVE WS-ACC-SS-TAX TO WS-DSP-AMOUNT
           DISPLAY '  Total Soc Security:  ' WS-DSP-AMOUNT
           MOVE WS-ACC-MEDICARE-TAX TO WS-DSP-AMOUNT
           DISPLAY '  Total Medicare:      ' WS-DSP-AMOUNT
           MOVE WS-ACC-DEDUCTIONS TO WS-DSP-AMOUNT
           DISPLAY '  Total Deductions:    ' WS-DSP-AMOUNT
           MOVE WS-ACC-NET-PAY TO WS-DSP-AMOUNT
           DISPLAY '  Total Net Pay:       ' WS-DSP-AMOUNT

           DISPLAY SPACES
           DISPLAY WS-C-REPORT-SEP

           IF WS-OVERFLOW-OCCURRED
               DISPLAY '*** WARNING: Overflow occurred during'
                   ' processing. Review error log. ***'
           END-IF
           .

Solution Walkthrough

Gross Pay Calculation: COMPUTE with Overflow Protection

The gross pay calculation demonstrates several key arithmetic principles from Chapter 6.

For hourly employees, the program first separates regular hours from overtime hours by comparing against the 80-hour biweekly threshold. Regular pay uses a straightforward multiplication:

           COMPUTE WS-WK-REG-PAY ROUNDED =
               WS-WK-REG-HOURS * WS-EMP-HOURLY-RATE

Overtime pay uses a three-factor multiplication that could easily overflow an undersized result field:

           COMPUTE WS-WK-OT-PAY ROUNDED =
               WS-WK-OT-HOURS * WS-EMP-HOURLY-RATE
               * WS-C-OVERTIME-MULT

The overtime multiplier (1.5) is stored as a named constant (WS-C-OVERTIME-MULT) rather than as a literal in the COMPUTE expression. When HMG later introduced double-time for holiday hours (2.0x), Marcus changed one VALUE clause rather than searching for every occurrence of 1.5 in the PROCEDURE DIVISION.

Every COMPUTE includes ROUNDED to ensure currency amounts are calculated to the nearest cent, and ON SIZE ERROR to catch any overflow. The overflow flag is a persistent indicator -- once set, it remains set for the entire batch run so the summary report can warn the payroll manager.

Progressive Tax Brackets: The Subtraction Approach

The federal tax calculation uses the "remaining income" approach to progressive taxation. Rather than calculating what falls in each bracket by comparing against bracket boundaries, the program subtracts each bracket's width from the remaining income:

  1. Start with the full annualized income in WS-WK-REMAINING-INC
  2. For each bracket, determine how much income falls in that bracket
  3. Multiply that portion by the bracket's rate (using ROUNDED)
  4. Subtract the bracket's width from the remaining income
  5. Continue to the next bracket until the remaining income is zero

This approach has two advantages over the alternative of using a sequence of IF tests against cumulative thresholds. First, the bracket calculations are independent -- each bracket's code follows exactly the same pattern, making it easy to add new brackets when tax law changes. Second, the running subtraction ensures that every dollar of income is taxed in exactly one bracket, eliminating the risk of double-counting that can occur with overlapping threshold ranges.

The annualization strategy (multiplying the biweekly gross by 26, calculating annual tax, then dividing by 26) matches the IRS-recommended withholding method and ensures that the per-period tax amount properly reflects the progressive rate structure.

FICA: The Social Security Wage Base Cap

The Social Security tax calculation demonstrates a business rule that requires careful arithmetic: the 6.2% tax applies only up to the annual wage base ($168,600 in 2024). The program must handle three scenarios:

  1. The employee has already exceeded the wage base in prior periods (no SS tax this period)
  2. The employee will exceed the wage base during this period (tax only on the remaining taxable amount)
  3. The employee is fully below the wage base (tax on the full gross)

Each scenario produces a different calculation, but all use ROUNDED and ON SIZE ERROR. The WS-SS-WAGE-CAPPED flag is set when the cap is reached, and the pay stub display uses this flag to print a notification.

Deductions: EVALUATE for Plan Selection

The deduction calculation uses EVALUATE TRUE with 88-level condition names to select the correct deduction amount based on the employee's benefit elections. This approach is cleaner than nested IF statements and directly maps the business rule: "If single coverage, deduct the single rate; if family coverage, deduct the family rate; if no coverage, deduct nothing."

The 401(k) contribution uses a percentage-of-gross calculation with ROUNDED:

           COMPUTE WS-WK-401K-DED ROUNDED =
               WS-WK-GROSS-PAY * (WS-EMP-401K-PCT / 100)

Note the parenthesized division by 100 to convert the percentage (e.g., 6.00) to a decimal (0.06). Without the parentheses, operator precedence would cause the multiplication to happen first, producing an incorrect result.

Net Pay: Signed Fields and Negative Detection

The net pay field uses PIC S9(07)V99 -- a signed field -- because deductions could theoretically exceed gross pay (for example, if a part-time employee with family coverage has a very short pay period). The program explicitly checks for negative net pay and displays a warning. This is a critical business rule: a negative paycheck requires manual intervention by the payroll department.

Lessons Learned

1. ROUNDED Is Non-Negotiable for Payroll

The original payroll program's inconsistent use of ROUNDED was the root cause of the penny discrepancies that triggered the rewrite. Marcus established a strict coding standard: every COMPUTE, MULTIPLY, and DIVIDE that produces a currency amount must include ROUNDED. The cost of including ROUNDED is negligible. The cost of omitting it is audit findings, employee complaints, and potential regulatory exposure.

2. ON SIZE ERROR Is Insurance, Not Overhead

Marcus included ON SIZE ERROR on every arithmetic statement, even those where overflow seems impossible. This "belt and suspenders" approach caught a real problem during testing: when processing an employee with an extremely high overtime rate (a machinist earning $62.50/hour working 40 hours of overtime), the overtime pay calculation overflowed a field that was originally sized as PIC 9(05)V99. Without ON SIZE ERROR, the program would have silently produced an incorrect paycheck. With it, the overflow was detected immediately and the field was resized.

3. Tax Bracket Constants Belong in WORKING-STORAGE, Not in Code

By storing all tax bracket thresholds and rates as named constants in WORKING-STORAGE, Marcus made annual tax updates a 15-minute task. When the IRS published new withholding tables for the following year, the developer updated the VALUE clauses in the constants section and the program was ready for testing. No PROCEDURE DIVISION logic needed to change.

4. Annualization Eliminates Per-Period Bracket Distortion

Applying progressive tax rates directly to biweekly gross pay would produce incorrect withholding. A biweekly paycheck of $3,000 would appear to be entirely in the lowest tax bracket, even though the annualized income of $78,000 spans multiple brackets. The annualization approach correctly distributes the tax burden across brackets and then converts back to a per-period amount.

5. The Signed Field for Net Pay Is Not Optional

Using an unsigned field for net pay masks a genuine business condition. When deductions exceed gross pay, the correct behavior is to detect the negative result and flag it for human review -- not to silently truncate it to zero or, worse, produce an incorrect positive amount. The signed PIC S9 field combined with the explicit negative-value check ensures that this edge case is handled correctly.

Discussion Questions

  1. The program calculates federal tax by annualizing the gross pay and then dividing the annual tax by 26. What problems could this approach cause for employees whose pay varies significantly from period to period (for example, due to commissions or large overtime weeks)? How would you address this?

  2. The Social Security wage base check uses year-to-date wages stored in the employee record. What would happen if the YTD wages field were not updated correctly after each payroll run? How would you design the system to prevent this class of error?

  3. Marcus chose to store tax bracket thresholds as individual named constants rather than as a table with OCCURS. What are the tradeoffs? At what point would a table-based approach become preferable?

  4. The 401(k) deduction is calculated as a percentage of gross pay. Some employees might want a flat dollar amount instead. How would you modify the data structure and calculation logic to support both methods?

  5. The program does not handle the Medicare additional tax (0.9% surtax above $200,000 in annual wages). How would you add this calculation? What WORKING-STORAGE changes would be needed? Where in the FICA calculation logic would the surtax calculation be inserted?

  6. If HMG expanded to a state with no income tax (such as Texas), what WORKING-STORAGE and PROCEDURE DIVISION changes would be needed to support multi-state payroll processing?

Connection to Chapter Concepts

This case study directly illustrates several key concepts from Chapter 6:

  • COMPUTE for complex expressions (Section: The COMPUTE Statement): The overtime pay calculation, 401(k) percentage, and annualization all use COMPUTE with multi-operator expressions.

  • ROUNDED on every currency calculation (Section: The ROUNDED Phrase): Every monetary COMPUTE includes ROUNDED to ensure cent-accurate results.

  • ON SIZE ERROR on every operation (Section: ON SIZE ERROR and NOT ON SIZE ERROR): Every arithmetic operation includes overflow protection with a persistent flag for batch-level reporting.

  • Progressive tax bracket arithmetic (Section: Financial Calculation Patterns): The "remaining income subtraction" approach demonstrates how to implement progressive taxation using sequential COMPUTE and SUBTRACT operations.

  • The GIVING phrase for operand preservation (Section: The GIVING Phrase and Operand Preservation): The ADD ... GIVING pattern is used in gross pay calculation to preserve both regular and overtime pay components for display.

  • Signed fields for potentially negative results (Section: Arithmetic with Signed Numbers): The net pay field uses PIC S9 to correctly handle the case where deductions exceed gross pay.