Project: A Payslip Calculator

COBOL GnuCOBOL 3.2.0 ยท โœ“ verified by execution on 2026-08-16

You have learned how to define data, calculate mathematics, make logical decisions, and format output. Now it is time to bring these foundations together.

In this capstone project, we will build a batch COBOL program that processes a simple payslip. It will take raw hours and rate, calculate overtime and taxes, and finally generate a formatted receipt.

The Architecture

A well-structured COBOL program isolates its tasks. Instead of writing one massive block of code, we use PERFORM to delegate work to specific paragraphs.

WORKING-STORAGE RAW DATA CALCULATED DATA EDITED FIELDS PROCEDURE DIVISION 200-CALCULATE-PAY 300-PRINT-PAYSLIP 100-MAIN

In 100-MAIN, we simply delegate tasks using PERFORM. It reads like an outline of the program.

Why COBOL Uses Global State

A modern programmer reading this architecture would immediately expect to pass parameters, like PERFORM 200-CALCULATE-PAY(HOURS, RATE). That intuition fails in COBOL because paragraphs are not functions. They have no parameters and no local scope. Everything defined in WORKING-STORAGE is perfectly global to the entire program.

Why did the language designers build it this way? In the mainframe era, allocating and deallocating memory on a call stack for millions of payroll records was unacceptably slow. By making all memory global and static, COBOL programs run at blistering speeds because memory addresses are resolved completely at compile time, eliminating runtime memory management.

The boundary where this rule stops applying is recursion. Because there is no call stack and no local variables, a standard COBOL paragraph cannot safely call itself without overwriting its own data. This is why COBOL is unmatched for flat batch processing, but completely unsuitable for recursive algorithms like walking tree data structures.

The Pitfall of Raw Data

A common misconception is trying to display computational fields directly. Because COBOL uses implied decimals (V) for math to ensure precision without wasting memory on the decimal character, you cannot print them directly if you want human readability.

Predict the output cobol

What gets displayed if we print the raw computational field?

       IDENTIFICATION DIVISION.
       PROGRAM-ID. RAW-PRINT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 RAW-PAY PIC 9(4)V99 VALUE 1500.50.
       PROCEDURE DIVISION.
           DISPLAY RAW-PAY.
           STOP RUN.
Output
1500.50

Without moving RAW-PAY to an edited picture like PIC $$$,$$9.99, COBOL dumps the memory contents unformatted, omitting commas and dollar signs, but GnuCOBOL retains the implied decimal in its display output.

The Complete Payslip Calculator

Here is the final, assembled project. We define our raw fields, calculated buffers, and edited print formats. Then, we execute the business logic and move the results to the display buffer.

cobol โœ“ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PAYSLIP.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  EMPLOYEE-DATA.
           05  EMP-NAME          PIC X(20) VALUE 'JANE DOE'.
           05  HOURS-WORKED      PIC 9(3)V99 VALUE 45.00.
           05  HOURLY-RATE       PIC 9(3)V99 VALUE 20.00.
       
       01  CALCULATED-FIELDS.
           05  REGULAR-HOURS     PIC 9(3)V99.
           05  OVERTIME-HOURS    PIC 9(3)V99.
           05  GROSS-PAY         PIC 9(5)V99.
           05  TAX-AMOUNT        PIC 9(5)V99.
           05  NET-PAY           PIC 9(5)V99.
           
       01  REPORT-FIELDS.
           05  PRT-NAME          PIC X(20).
           05  PRT-GROSS         PIC $$$,$$9.99.
           05  PRT-TAX           PIC $$$,$$9.99.
           05  PRT-NET           PIC $$$,$$9.99.

       PROCEDURE DIVISION.
       100-MAIN.
           PERFORM 200-CALCULATE-PAY.
           PERFORM 300-PRINT-PAYSLIP.
           STOP RUN.
           
       200-CALCULATE-PAY.
           IF HOURS-WORKED > 40
               MOVE 40 TO REGULAR-HOURS
               COMPUTE OVERTIME-HOURS = HOURS-WORKED - 40
               COMPUTE GROSS-PAY = (REGULAR-HOURS * HOURLY-RATE) +
                                   (OVERTIME-HOURS * (HOURLY-RATE * 1.5))
           ELSE
               MOVE HOURS-WORKED TO REGULAR-HOURS
               MOVE 0 TO OVERTIME-HOURS
               COMPUTE GROSS-PAY = REGULAR-HOURS * HOURLY-RATE
           END-IF.
           
           IF GROSS-PAY > 800
               COMPUTE TAX-AMOUNT = GROSS-PAY * 0.20
           ELSE
               COMPUTE TAX-AMOUNT = GROSS-PAY * 0.10
           END-IF.
           
           COMPUTE NET-PAY = GROSS-PAY - TAX-AMOUNT.
           
       300-PRINT-PAYSLIP.
           MOVE EMP-NAME TO PRT-NAME.
           MOVE GROSS-PAY TO PRT-GROSS.
           MOVE TAX-AMOUNT TO PRT-TAX.
           MOVE NET-PAY TO PRT-NET.
           DISPLAY '============================'.
           DISPLAY '      PAYSLIP REPORT        '.
           DISPLAY '============================'.
           DISPLAY 'EMPLOYEE:   ' PRT-NAME.
           DISPLAY 'GROSS PAY: ' PRT-GROSS.
           DISPLAY 'TAX DEDUCT:' PRT-TAX.
           DISPLAY 'NET PAY:   ' PRT-NET.
           DISPLAY '============================'.
Output
============================
      PAYSLIP REPORT        
============================
EMPLOYEE:   JANE DOE            
GROSS PAY:    $950.00
TAX DEDUCT:   $190.00
NET PAY:      $760.00
============================

Try it yourself

Now it is your turn to modify the project components.

Congratulations on building your first substantial COBOL application! You now possess the foundational knowledge to read and trace standard batch COBOL programs.

Check yourself

Why do we move computational fields with implied decimals (like `PIC 9(5)V99`) to edited picture fields before displaying them?

Reveal answer

Because computational fields will print as raw digits without the decimal point, making them hard for humans to read. โ€” Computational fields with implied decimals (`V`) will print exactly as raw digits without the decimal point. You must MOVE them to an edited picture field (like `PIC $$$,$$9.99`) before displaying to add commas, dollar signs, and decimal points.

How does the PROCEDURE DIVISION handle execution flow by default?

Reveal answer

It executes top to bottom and can fall through into other paragraphs unless explicitly stopped. โ€” While it can fall through, the standard COBOL design pattern uses a main driver paragraph (like `100-MAIN`) that `PERFORM`s other paragraphs and then ends with `STOP RUN`, preventing accidental fall-through.

What happens if an edited picture is not large enough to hold the output, such as moving 1000 into PIC $9.99?

Reveal answer

The value will be truncated and only the rightmost digits will fit. โ€” COBOL truncates data if the destination variable is too small. You must ensure your edited picture has enough digit places (9s or Zs) to hold the maximum expected value.

Challenges

Challenge 1 +25 XP

Update the tax calculation so that any `GROSS-PAY` over 1000 is taxed at 25%, and everything else is taxed at 10%.

cobol โœ“ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real โ€” it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 โ€” expects "TAX: $300.00\n"
Need a hint? (โˆ’25% XP)

Change the condition to check if GROSS-PAY > 1000, and multiply by 0.25.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. CHAL-TAX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  GROSS-PAY         PIC 9(5)V99 VALUE 1200.00.
01  TAX-AMOUNT        PIC 9(5)V99.
01  PRT-TAX           PIC $$$,$$9.99.
PROCEDURE DIVISION.
    IF GROSS-PAY > 1000
        COMPUTE TAX-AMOUNT = GROSS-PAY * 0.25
    ELSE
        COMPUTE TAX-AMOUNT = GROSS-PAY * 0.10
    END-IF.
    MOVE TAX-AMOUNT TO PRT-TAX.
    DISPLAY 'TAX: ' PRT-TAX.
    STOP RUN.

Challenge 2 +25 XP

The `NET-PAY` below has an implied decimal. Define `PRT-NET` with an edited picture that displays it as a floating dollar amount with commas (e.g. $1,550.50).

cobol โœ“ solution verified at build time

COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the fixed code should be, then open the solution and compare. The expected output below is real โ€” it came from compiling the solution with GnuCOBOL when this page was built.

  • Test 1 โ€” expects " $1,550.50\n"
Need a hint? (โˆ’25% XP)

Use the picture format `$$$,$$9.99`.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. CHAL-PIC.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  NET-PAY           PIC 9(5)V99 VALUE 1550.50.
01  PRT-NET           PIC $$$,$$9.99.
PROCEDURE DIVISION.
    MOVE NET-PAY TO PRT-NET.
    DISPLAY PRT-NET.
    STOP RUN.