Chapter 38 Exercises: Capstone Project

These exercises extend the PennyWise 2.0 capstone project. Unlike previous chapters, there is no Part A (conceptual) or Part B (analysis) — this is all building. Each exercise adds a real feature to a real application.


Part C: Core Implementation Exercises

C.1. Complete the Budget Dialog

Implement a TBudgetDialog form that allows the user to set a monthly budget for a specific category. The dialog should include:

  • A TComboBox populated with all existing categories.
  • A TFloatSpinEdit for the budget amount.
  • Month and year selectors (TSpinEdit for each).
  • OK and Cancel buttons.

When the user clicks OK, the dialog should call TTransactionManager.SetBudget and TFinanceDatabase.SaveBudget, then close.

Guidance Create a new form unit (BudgetDialog.pas) with a TBudgetDialog = class(TForm). Populate the category combo box from the existing transactions in TTransactionManager — use a TStringList to collect unique categories. The dialog should be shown modally with ShowModal, returning mrOK or mrCancel. The calling code in FinanceUI checks the result and acts accordingly.

C.2. Implement Transaction Editing

Complete the BtnUpdateClick handler in TMainForm. When the user selects a row in the grid and clicks "Update," the handler should:

  1. Read the selected row's ID from the grid.
  2. Find the corresponding TTransaction via TTransactionManager.FindTransaction.
  3. Update the transaction's fields from the entry panel controls.
  4. Call TFinanceDatabase.UpdateTransaction to persist the change.
  5. Refresh the grid, budget panel, and charts.
Guidance The key challenge is mapping from the entry panel back to the transaction object. You need to handle the type correctly — if the user changes a transaction from expense to income (or vice versa), you need to delete the old transaction and create a new one of the correct type, because TExpense and TIncome are different classes. For simple edits (changing the amount, date, category, or description), an in-place update works.

C.3. Implement the Search Feature

Add a search bar to TMainForm that filters the transaction grid by description, category, or amount range. The search should:

  • Accept a text query that matches against description and category (case-insensitive substring match).
  • Support amount range filters with syntax like >100 or 50-200.
  • Update the grid in real-time as the user types (use the OnChange event of a TEdit).
  • Show a "Clear" button that resets the filter and shows all transactions.
Guidance Add a FilterTransactions method to TTransactionManager that returns a TTransactionList of matching transactions. Parse the search query to detect amount ranges (look for >, <, or - characters and TryStrToFloat on the remaining text). For text matching, use Pos(LowerCase(Query), LowerCase(T.Description)). Refresh the grid with only the filtered results, but do not delete the non-matching transactions from the manager.

C.4. Implement the Date Range Picker

Add a date range picker to the main form that allows the user to view transactions for a specific period (not just the current month). Include:

  • Two TDateTimePicker controls for start and end dates.
  • Preset buttons: "This Month," "Last Month," "Last 3 Months," "This Year," "All."
  • Update all displays (grid, charts, budget panel, status bar) when the range changes.
Guidance Replace the FCurrentMonth and FCurrentYear fields with FStartDate and FEndDate fields. Update all query methods to accept date ranges instead of month/year pairs. The budget panel should show the budget for the month containing FEndDate. The charts should aggregate across the selected range.

Part D: Advanced Feature Exercises

D.1. Implement Recurring Transactions

Build a recurring transaction system:

  1. Add a "Recurring" checkbox and a "Repeat every N days" spin edit to the entry panel.
  2. When the application starts, check for any recurring expenses whose next occurrence has passed and automatically create the missing entries.
  3. Add a "Manage Recurring" dialog that lists all recurring transactions with options to pause, resume, or delete them.
Guidance The data model already supports recurring transactions (TExpense.IsRecurring and TExpense.RecurrenceDays). The challenge is the scheduling logic. On startup, query all recurring expenses, find the most recent instance of each, and calculate whether new instances should have been created since then. Use a while loop: while NextOccurrence <= Today, create a new expense and advance NextOccurrence by RecurrenceDays.

D.2. Implement the REST API Endpoint

Using the networking techniques from Chapter 35, implement a local HTTP server that serves PennyWise data as JSON:

  • GET /api/transactions — returns all transactions as a JSON array.
  • GET /api/transactions?month=3&year=2026 — returns filtered transactions.
  • GET /api/summary?month=3&year=2026 — returns a monthly summary.
  • GET /api/budgets — returns all budgets.

The server should run on a background thread, listening on localhost:8080 by default.

Guidance Use the fphttpserver unit from Free Pascal's fcl-web package. Create a TFinanceAPIServer class that wraps TFPHTTPServer. In the request handler, parse the URL path and query parameters, call the appropriate TTransactionManager method, and return JSON. Use TJSONExporter as a reference for JSON formatting, or use the fpjson unit for proper JSON generation.

D.3. Implement Data Encryption

Add optional encryption to the PennyWise database:

  1. On first run, prompt the user to set a master password (or skip encryption).
  2. Derive an encryption key from the password using PBKDF2 or a similar key derivation function.
  3. Encrypt the database file on close and decrypt it on open.
  4. Store a hash of the password (salted, using SHA-256) in a separate settings file to verify the password on subsequent launches.
Guidance Free Pascal includes the DCPcrypt package (available via OPM) which provides AES encryption and SHA-256 hashing. A simpler approach for a first implementation: encrypt the entire SQLite file with AES-256-CBC on close and decrypt it on open. More sophisticated: use SQLCipher, a SQLite extension that provides transparent encryption. For the key derivation, use PBKDF2 with at least 100,000 iterations.

D.4. Build a Report Printing System

Implement a report printing feature:

  1. Generate a formatted monthly report with headers, category breakdowns, and summary totals.
  2. Use the LCL's TPrinter or TPrintDialog to send the report to a printer.
  3. Alternatively, generate a PDF using the fcl-pdf library included with Free Pascal.
  4. Add a "Print Preview" dialog that shows the report before printing.
Guidance The fcl-pdf library (fpPDF unit) creates PDF documents programmatically. Create a TReportGenerator class that takes a TMonthlyReport record and produces a PDF with a title, date, table of expenses by category, a summary row, and a footer. For print preview, render the same data onto a TImage or TPaintBox canvas.

Part E: Software Engineering Exercises

E.1. Set Up Version Control

Initialize a Git repository for PennyWise 2.0:

  1. Create a .gitignore file that excludes compiled binaries (*.exe, *.o, *.ppu, *.compiled), IDE files (*.lps), and the database file (*.db).
  2. Make an initial commit with the message "PennyWise 2.0: initial capstone project."
  3. Create feature branches for each major feature (CSV import, REST API, system tray).
  4. Write meaningful commit messages that describe why a change was made, not just what changed.
Guidance Good commit messages follow the pattern: a short summary line (under 72 characters), a blank line, then a longer explanation. Example: "Add CSV import with configurable column mapping\n\nRosa's bank exports semicolons and DD/MM/YYYY dates, Tomas's uses\ncommas and MM/DD/YYYY. The importer now accepts configurable delimiters\nand tries multiple date formats." This is a professional skill that transfers to every language and every project.

E.2. Write Documentation

Create user documentation for PennyWise 2.0:

  1. A README.md with project description, build instructions, and screenshots.
  2. An INSTALL.md with step-by-step installation instructions for Windows, Linux, and macOS.
  3. A USER-GUIDE.md with instructions for every feature: adding transactions, setting budgets, importing CSV, exporting reports.
  4. A DEVELOPER.md explaining the architecture, unit structure, and how to add new features.
Guidance Write documentation for two audiences: users (who do not know or care about the code) and developers (who need to understand the architecture). The user guide should include annotated screenshots showing each step. The developer guide should include the module diagram from Section 38.2 and explain the relationships between units. Good documentation is a discipline — like testing, it feels optional until you need it.

E.3. Continuous Integration

Set up a CI/CD pipeline for PennyWise 2.0 using GitHub Actions:

  1. Create a .github/workflows/build.yml file that builds the project on push.
  2. Run all unit tests after the build.
  3. Produce a build artifact (the compiled binary) that can be downloaded.
  4. Add a badge to the README showing the build status.
Guidance The Free Pascal compiler can be installed in a GitHub Actions workflow using setup-lazarus or by installing fpc directly. The workflow should: (1) check out the code, (2) install Free Pascal and Lazarus, (3) run lazbuild PennyWise2.lpi, (4) run the test binary, (5) upload the compiled binary as an artifact. This is how professional open-source projects maintain quality across contributions.

Part F: Capstone Reflection

F.1. Write a 500-word reflection on your experience building PennyWise 2.0. Address:

  • What was the hardest part? Why?
  • What would you do differently if you started over?
  • Which concept from the textbook turned out to be more important than you expected?
  • Which concept turned out to be less important?
  • How has your understanding of programming changed from Chapter 1 to Chapter 38?

F.2. If you were going to build PennyWise 3.0, what three features would you add? For each feature, identify which chapters from this textbook provide the foundational knowledge and what new learning would be required.

F.3. Rosa and Tomas both learned Pascal as a first (or early) programming language. Write a letter to a friend who is considering learning to program, explaining why you would (or would not) recommend starting with Pascal. Be honest — acknowledge both the strengths and the limitations.