Case Study 1: Designing a Point-of-Sale Receipt

The Scenario

Rosa Martinelli's freelance graphic design business is growing. She has started selling design templates and printed materials at local craft fairs, and she needs a simple receipt program for her laptop — something she can run at a booth to print a receipt for each customer. The receipt must look professional: clean columns, proper alignment, calculated totals, and a format that customers recognize and trust.

This case study walks through the design and implementation of a point-of-sale receipt generator using only the I/O and formatting techniques from Chapter 4.

The Requirements

Rosa sketches what she wants the receipt to look like:

============================================
        ROSA MARTINELLI DESIGNS
       Hand-crafted digital templates
        www.rosamartinelli.design
============================================

Date: 03/23/2026            Receipt #: 1047

Item                    Qty   Unit Price    Total
-------------------------------------------------
Business Card Template    2       $12.50   $25.00
Wedding Invite Pack       1       $35.00   $35.00
Logo Design (Basic)       1       $75.00   $75.00
-------------------------------------------------
                              Subtotal:  $135.00
                              Tax (7%):    $9.45
                              ==================
                              TOTAL:     $144.45

Payment method: Cash

    Thank you for your purchase!
    Follow us @rosamartinelli
============================================

The key formatting challenges are: 1. A centered header block 2. Date and receipt number on the same line, separated by whitespace 3. A four-column item table with right-aligned numbers 4. Calculated subtotal, tax, and grand total 5. A clean footer

The Design Process

Step 1: Plan the Column Widths

Before writing a single line of code, Rosa maps out her columns:

Column Content Width Alignment
Item description Up to 24 characters 24 Left (using padding)
Quantity 1-99 4 Right
Unit price Up to $999.99 12 Right, 2 decimals
Line total Up to $9999.99 8 Right, 2 decimals

Total width: 24 + 4 + 12 + 8 = 48 characters. She rounds up to 48 for the separator line.

Step 2: Identify the Variables

var
  { Item 1 }
  item1Name: String;
  item1Qty: Integer;
  item1Price: Real;
  item1Total: Real;

  { Item 2 }
  item2Name: String;
  item2Qty: Integer;
  item2Price: Real;
  item2Total: Real;

  { Item 3 }
  item3Name: String;
  item3Qty: Integer;
  item3Price: Real;
  item3Total: Real;

  { Summary }
  subtotal: Real;
  taxRate: Real;
  taxAmount: Real;
  grandTotal: Real;
  receiptNum: Integer;
  paymentMethod: String;

Step 3: Input Phase

Rosa decides the program should prompt for each item interactively:

{ Read item 1 }
WriteLn('--- Item 1 ---');
Write('  Description: ');
ReadLn(item1Name);
Write('  Quantity: ');
ReadLn(item1Qty);
Write('  Unit price: $');
ReadLn(item1Price);
item1Total := item1Qty * item1Price;

She repeats this pattern for items 2 and 3. (She notes that in Chapter 6, she will replace this repetition with a loop, and in Chapter 9, she will store items in an array.)

Step 4: Calculation Phase

subtotal := item1Total + item2Total + item3Total;
taxRate := 0.07;
taxAmount := subtotal * taxRate;
grandTotal := subtotal + taxAmount;

Step 5: Output Phase — The Receipt

This is where formatting mastery matters. Let us examine each section:

The header uses hardcoded centered text. Rosa counts characters to center each line within 48 characters:

WriteLn('================================================');
WriteLn('        ROSA MARTINELLI DESIGNS                  ');
WriteLn('       Hand-crafted digital templates             ');
WriteLn('        www.rosamartinelli.design                 ');
WriteLn('================================================');

The date and receipt number use field widths to push the receipt number to the right:

WriteLn;
Write('Date: 03/23/2026');
WriteLn('Receipt #: ':24, receiptNum:4);

The table header establishes the column positions:

WriteLn;
WriteLn('Item':24, 'Qty':4, 'Unit Price':12, 'Total':8);
WriteLn('-------------------------------------------------');

Each item line uses the same field widths. The trick for left-aligning the item name is to use Write to output it, then pad to the column width:

Write(item1Name);
Write('':24 - Length(item1Name));    { pad to 24 chars }
WriteLn(item1Qty:4, item1Price:12:2, item1Total:8:2);

The summary section right-aligns the labels and values:

WriteLn('-------------------------------------------------');
WriteLn('Subtotal:':40, subtotal:8:2);
Write('Tax (');
Write(taxRate * 100:0:0);
Write('%):');
WriteLn('':32, taxAmount:8:2);
WriteLn('==================':40, '========');
WriteLn('TOTAL:':40, grandTotal:8:2);

The Complete Program

program PointOfSale;
{ Rosa's Point-of-Sale Receipt Generator }
{ Chapter 4 Case Study 1 }

var
  item1Name, item2Name, item3Name: String;
  item1Qty, item2Qty, item3Qty: Integer;
  item1Price, item2Price, item3Price: Real;
  item1Total, item2Total, item3Total: Real;
  subtotal, taxAmount, grandTotal: Real;
  receiptNum: Integer;
  paymentMethod: String;
  inputStr: String;
  valCode: Integer;

const
  TaxRate = 0.07;
  LineWidth = 48;

begin
  { --- Input Phase --- }
  WriteLn('=== RECEIPT ENTRY ===');
  WriteLn;

  Write('Receipt number: ');
  ReadLn(receiptNum);

  WriteLn;
  WriteLn('--- Item 1 ---');
  Write('  Description: ');
  ReadLn(item1Name);
  repeat
    Write('  Quantity:    ');
    ReadLn(inputStr);
    Val(inputStr, item1Qty, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  repeat
    Write('  Unit price: $');
    ReadLn(inputStr);
    Val(inputStr, item1Price, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  item1Total := item1Qty * item1Price;

  WriteLn;
  WriteLn('--- Item 2 ---');
  Write('  Description: ');
  ReadLn(item2Name);
  repeat
    Write('  Quantity:    ');
    ReadLn(inputStr);
    Val(inputStr, item2Qty, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  repeat
    Write('  Unit price: $');
    ReadLn(inputStr);
    Val(inputStr, item2Price, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  item2Total := item2Qty * item2Price;

  WriteLn;
  WriteLn('--- Item 3 ---');
  Write('  Description: ');
  ReadLn(item3Name);
  repeat
    Write('  Quantity:    ');
    ReadLn(inputStr);
    Val(inputStr, item3Qty, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  repeat
    Write('  Unit price: $');
    ReadLn(inputStr);
    Val(inputStr, item3Price, valCode);
    if valCode <> 0 then WriteLn('  Invalid number.');
  until valCode = 0;
  item3Total := item3Qty * item3Price;

  WriteLn;
  Write('Payment method (Cash/Card): ');
  ReadLn(paymentMethod);

  { --- Calculation Phase --- }
  subtotal := item1Total + item2Total + item3Total;
  taxAmount := subtotal * TaxRate;
  grandTotal := subtotal + taxAmount;

  { --- Output Phase: The Receipt --- }
  WriteLn;
  WriteLn;
  WriteLn('================================================');
  WriteLn('        ROSA MARTINELLI DESIGNS');
  WriteLn('       Hand-crafted digital templates');
  WriteLn('        www.rosamartinelli.design');
  WriteLn('================================================');
  WriteLn;
  Write('Date: 03/23/2026');
  WriteLn('Receipt #:':20, receiptNum:5);
  WriteLn;
  Write('Item');
  Write('':20);
  WriteLn('Qty':4, 'Unit Price':12, 'Total':8);
  WriteLn('------------------------------------------------');

  { Item 1 }
  Write(item1Name);
  if Length(item1Name) < 24 then
    Write('':24 - Length(item1Name));
  WriteLn(item1Qty:4, item1Price:12:2, item1Total:8:2);

  { Item 2 }
  Write(item2Name);
  if Length(item2Name) < 24 then
    Write('':24 - Length(item2Name));
  WriteLn(item2Qty:4, item2Price:12:2, item2Total:8:2);

  { Item 3 }
  Write(item3Name);
  if Length(item3Name) < 24 then
    Write('':24 - Length(item3Name));
  WriteLn(item3Qty:4, item3Price:12:2, item3Total:8:2);

  WriteLn('------------------------------------------------');
  WriteLn('Subtotal:':40, subtotal:8:2);
  WriteLn('Tax (7%):':40, taxAmount:8:2);
  WriteLn('':32, '================');
  WriteLn('TOTAL:':40, grandTotal:8:2);
  WriteLn;
  WriteLn('Payment method: ', paymentMethod);
  WriteLn;
  WriteLn('    Thank you for your purchase!');
  WriteLn('    Follow us @rosamartinelli');
  WriteLn('================================================');
end.

Discussion Questions

  1. Column width planning. Why is it important to decide on column widths before writing the output code? What happens if you change a column width after writing all the output statements?

  2. Left vs. right alignment. In the receipt, item descriptions are left-aligned while prices are right-aligned. Why is this the conventional choice? How would the receipt look if prices were left-aligned?

  3. Repetition. The input code for each item is nearly identical. How would you reduce this repetition? (Think ahead to Chapter 7's procedures and Chapter 6's loops.)

  4. Edge cases. What happens if an item description is longer than 24 characters? What happens if a price exceeds $999.99? How would you handle these cases?

  5. Tax calculation. The tax amount might have rounding issues (e.g., 7% of $135.00 is $9.45 exactly, but other amounts might produce fractions of a cent). How should a receipt program handle rounding? What is the professional standard?

  6. Hardcoded date. The date in the receipt is hardcoded as 03/23/2026. How would you make it dynamic? (Hint: Free Pascal's SysUtils unit provides date/time functions — you will encounter them in later chapters.)

Key Lessons

  • Plan before you code. Rosa mapped out her column widths on paper before writing a single line of Pascal. This saved her from constant trial-and-error adjustments.
  • Consistency is king. Every item line uses the exact same field widths. If you change one, you must change them all. This is why constants (like a ColWidth constant) are valuable.
  • The padding trick. Writing '':N - Length(s) is a classic Pascal idiom for left-justifying a string in a field of width N. It outputs N - Length(s) spaces after the string.
  • Separation of phases. The program has three clear phases: input, calculation, output. This structure makes the code easy to understand and modify.