Case Study 1: Simulating a Cash Register

Overview

In this case study, we build a complete cash register simulation that exercises all three of Pascal's loop constructs in a single, cohesive program. A cashier scans items for a customer, the register prints a receipt, and then the next customer steps up. This mirrors the real-world flow of a retail checkout and demonstrates how different loop types naturally fit different aspects of the same problem.

Loop constructs used: - REPEAT..UNTIL — scanning items for a single customer (at least one item per transaction) - FOR loop — printing the itemized receipt - WHILE..DO — processing the queue of customers (which might be empty)


Problem Statement

Write a program that simulates a cash register with the following behavior:

  1. The cashier enters the number of customers waiting in line.
  2. For each customer: a. The cashier scans items by entering item names and prices. b. Entering a price of 0 signals the end of that customer's items. c. The register prints an itemized receipt with subtotal, tax (8.5%), and total.
  3. After all customers are processed, the register prints a shift summary showing total customers served, total items scanned, and total revenue.

Design

Data Structures

We will store each customer's items in arrays (up to 50 items per transaction). Each item has a name and a price.

Loop Structure

Read CustomerCount

WHILE CustomerCount > 0:
  For each customer:
    REPEAT:
      Read item name and price
    UNTIL price = 0

    FOR i := 1 to ItemCount:
      Print receipt line

    Decrement CustomerCount

Print shift summary

The outer WHILE loop handles the customer queue. If the cashier enters 0 customers, the program skips directly to the summary (correctly handling an empty queue). Inside it, the REPEAT..UNTIL loop handles item scanning — every transaction has at least one item. The FOR loop prints the receipt, iterating over the exact number of recorded items.


Implementation

program CashRegister;

const
  TAX_RATE = 0.085;
  MAX_ITEMS = 50;

var
  { Item arrays for current transaction }
  ItemNames:  array[1..MAX_ITEMS] of string;
  ItemPrices: array[1..MAX_ITEMS] of Real;

  { Transaction variables }
  ItemCount:     Integer;
  Price:         Real;
  Subtotal:      Real;
  Tax:           Real;
  Total:         Real;
  ItemName:      string;

  { Queue variables }
  TotalCustomers:  Integer;
  CustomersLeft:   Integer;
  CustomerNumber:  Integer;

  { Shift summary variables }
  ShiftRevenue:    Real;
  ShiftItems:      Integer;

  { Loop variable }
  i: Integer;

begin
  WriteLn('================================');
  WriteLn('   PASCAL MART CASH REGISTER');
  WriteLn('================================');
  WriteLn;

  { Initialize shift totals }
  ShiftRevenue := 0;
  ShiftItems := 0;

  { Get number of customers }
  Write('How many customers in line? ');
  ReadLn(CustomersLeft);

  TotalCustomers := CustomersLeft;
  CustomerNumber := 0;

  { ===== OUTER LOOP: Customer Queue (WHILE) ===== }
  { Using WHILE because there might be zero customers }
  while CustomersLeft > 0 do
  begin
    CustomerNumber := CustomerNumber + 1;
    WriteLn;
    WriteLn('--- Customer ', CustomerNumber, ' ---');
    WriteLn('Scan items (enter $0.00 to finish):');

    ItemCount := 0;

    { ===== INNER LOOP 1: Item Scanning (REPEAT) ===== }
    { Using REPEAT because every transaction has at least one item }
    repeat
      Write('  Item name: ');
      ReadLn(ItemName);
      Write('  Price: $');
      ReadLn(Price);

      if Price > 0 then
      begin
        ItemCount := ItemCount + 1;
        if ItemCount <= MAX_ITEMS then
        begin
          ItemNames[ItemCount] := ItemName;
          ItemPrices[ItemCount] := Price;
        end
        else
          WriteLn('  WARNING: Maximum items reached!');
      end
      else if Price < 0 then
        WriteLn('  Price cannot be negative. Item not added.');

    until Price <= 0;

    { ===== Print Receipt ===== }
    if ItemCount > 0 then
    begin
      WriteLn;
      WriteLn('  ==============================');
      WriteLn('       PASCAL MART RECEIPT');
      WriteLn('  ==============================');

      Subtotal := 0;

      { ===== INNER LOOP 2: Receipt Printing (FOR) ===== }
      { Using FOR because we know exactly how many items }
      for i := 1 to ItemCount do
      begin
        WriteLn('  ', ItemNames[i]:20, ' $', ItemPrices[i]:7:2);
        Subtotal := Subtotal + ItemPrices[i];
      end;

      Tax := Subtotal * TAX_RATE;
      Total := Subtotal + Tax;

      WriteLn('  ------------------------------');
      WriteLn('  ', 'Subtotal':20, ' $', Subtotal:7:2);
      WriteLn('  ', 'Tax (8.5%)':20, ' $', Tax:7:2);
      WriteLn('  ==============================');
      WriteLn('  ', 'TOTAL':20, ' $', Total:7:2);
      WriteLn('  ==============================');
      WriteLn;

      { Update shift totals }
      ShiftRevenue := ShiftRevenue + Total;
      ShiftItems := ShiftItems + ItemCount;
    end
    else
      WriteLn('  No items scanned. Transaction voided.');

    CustomersLeft := CustomersLeft - 1;

    if CustomersLeft > 0 then
      WriteLn('  Next customer, please! (', CustomersLeft, ' remaining)');
  end;

  { ===== Shift Summary ===== }
  WriteLn;
  WriteLn('================================');
  WriteLn('       SHIFT SUMMARY');
  WriteLn('================================');
  WriteLn('  Customers served: ', TotalCustomers);
  WriteLn('  Items scanned:    ', ShiftItems);
  WriteLn('  Total revenue:    $', ShiftRevenue:0:2);
  WriteLn('================================');
end.

Sample Run

================================
   PASCAL MART CASH REGISTER
================================

How many customers in line? 2

--- Customer 1 ---
Scan items (enter $0.00 to finish):
  Item name: Milk
  Price: $3.49
  Item name: Bread
  Price: $2.99
  Item name: Eggs
  Price: $4.25
  Item name: done
  Price: $0

  ==============================
       PASCAL MART RECEIPT
  ==============================
                  Milk    $   3.49
                 Bread    $   2.99
                  Eggs    $   4.25
  ------------------------------
              Subtotal    $  10.73
            Tax (8.5%)    $   0.91
  ==============================
                 TOTAL    $  11.64
  ==============================

  Next customer, please! (1 remaining)

--- Customer 2 ---
Scan items (enter $0.00 to finish):
  Item name: Coffee
  Price: $8.99
  Item name: done
  Price: $0

  ==============================
       PASCAL MART RECEIPT
  ==============================
                Coffee    $   8.99
  ------------------------------
              Subtotal    $   8.99
            Tax (8.5%)    $   0.76
  ==============================
                 TOTAL    $   9.75
  ==============================


================================
       SHIFT SUMMARY
================================
  Customers served: 2
  Items scanned:    4
  Total revenue:    $21.39
================================

Analysis

Why Each Loop Type Fits

Task Loop Used Why This Loop
Customer queue WHILE..DO The number of customers might be zero. If the cashier enters 0, we skip straight to the summary. A REPEAT..UNTIL loop would try to process a nonexistent customer.
Item scanning REPEAT..UNTIL Every real transaction involves scanning at least one item before we can check whether the cashier is done. The "scan then check" pattern is a natural post-test situation.
Receipt printing FOR After scanning, we know exactly how many items were recorded (ItemCount). A counted loop is the cleanest way to iterate through them.

Patterns Used

  • Accumulator pattern: Subtotal accumulates item prices; ShiftRevenue accumulates transaction totals.
  • Counter pattern: ItemCount tracks items per transaction; ShiftItems tracks items per shift.
  • Sentinel loop: Price of $0 serves as the sentinel for ending item entry.
  • Input validation: Negative prices are caught and rejected (though a more robust version would use a REPEAT..UNTIL validation loop for each price).

Possible Enhancements

  1. Discount codes: Add a prompt after scanning that applies a percentage discount before tax.
  2. Payment processing: After printing the total, ask for the payment amount and compute change using a REPEAT..UNTIL loop (ensure payment >= total).
  3. Item categories: Track whether items are taxable or tax-exempt (groceries vs. prepared food).
  4. Receipt numbering: Use CustomerNumber as the receipt number.

Exercises

  1. Add a payment step after receipt printing. Use a REPEAT..UNTIL loop to ensure the customer pays enough, then display the change.
  2. Add a "void last item" command. If the cashier enters "VOID" as the item name, remove the last scanned item from the arrays and decrement ItemCount.
  3. Modify the program so that the cashier does not need to enter the number of customers up front. Instead, after each customer, ask "Is there another customer? (Y/N)". Which loop type should you use for this outer loop?
  4. Add a running subtotal that displays after each item is scanned, so the cashier can see the accumulating total in real time.