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
TComboBoxpopulated with all existing categories. - A
TFloatSpinEditfor the budget amount. - Month and year selectors (
TSpinEditfor 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:
- Read the selected row's ID from the grid.
- Find the corresponding
TTransactionviaTTransactionManager.FindTransaction. - Update the transaction's fields from the entry panel controls.
- Call
TFinanceDatabase.UpdateTransactionto persist the change. - 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, becauseTExpense 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
>100or50-200. - Update the grid in real-time as the user types (use the
OnChangeevent of aTEdit). - Show a "Clear" button that resets the filter and shows all transactions.
Guidance
Add aFilterTransactions 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
TDateTimePickercontrols 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 theFCurrentMonth 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:
- Add a "Recurring" checkbox and a "Repeat every N days" spin edit to the entry panel.
- When the application starts, check for any recurring expenses whose next occurrence has passed and automatically create the missing entries.
- 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 thefphttpserver 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:
- On first run, prompt the user to set a master password (or skip encryption).
- Derive an encryption key from the password using PBKDF2 or a similar key derivation function.
- Encrypt the database file on close and decrypt it on open.
- 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 theDCPcrypt 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:
- Generate a formatted monthly report with headers, category breakdowns, and summary totals.
- Use the LCL's
TPrinterorTPrintDialogto send the report to a printer. - Alternatively, generate a PDF using the
fcl-pdflibrary included with Free Pascal. - Add a "Print Preview" dialog that shows the report before printing.
Guidance
Thefcl-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:
- Create a
.gitignorefile that excludes compiled binaries (*.exe,*.o,*.ppu,*.compiled), IDE files (*.lps), and the database file (*.db). - Make an initial commit with the message "PennyWise 2.0: initial capstone project."
- Create feature branches for each major feature (CSV import, REST API, system tray).
- 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:
- A
README.mdwith project description, build instructions, and screenshots. - An
INSTALL.mdwith step-by-step installation instructions for Windows, Linux, and macOS. - A
USER-GUIDE.mdwith instructions for every feature: adding transactions, setting budgets, importing CSV, exporting reports. - A
DEVELOPER.mdexplaining 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:
- Create a
.github/workflows/build.ymlfile that builds the project on push. - Run all unit tests after the build.
- Produce a build artifact (the compiled binary) that can be downloaded.
- Add a badge to the README showing the build status.
Guidance
The Free Pascal compiler can be installed in a GitHub Actions workflow usingsetup-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.