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
-
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?
-
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?
-
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.)
-
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?
-
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?
-
Hardcoded date. The date in the receipt is hardcoded as
03/23/2026. How would you make it dynamic? (Hint: Free Pascal'sSysUtilsunit 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
ColWidthconstant) 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 outputsN - 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.