Intermediate Project 8 — Payroll System¶
Difficulty: Intermediate · Estimated effort: 14–18 hours · Prerequisites: Module 6 — Inheritance, Module 7 — Polymorphism, Module 12 — Templates, Module 14 — The STL with OOP · Combines: several kinds of employee, one pay run, a creation point, money as a type
This is one of the designated integration projects. It must use, explicitly and in writing: multiple classes · relationships between classes · encapsulation · inheritance or composition · polymorphism · the STL · exception handling.
No complete solution is provided. The employee kinds are described by their pay rules, not by a class list.
1. Scenario¶
A small firm pays four kinds of people: salaried staff, hourly workers, commission-only sales staff, and hourly workers who also earn commission. The pay run happens monthly, prints a payslip per person, and must total to the same figure as the sum of the individual pays — a reconciliation the firm checks by hand today and gets wrong about once a year.
2. Problem statement¶
Build the payroll system: employee types with genuinely different pay rules, a monthly run producing payslips and totals, deduction rules applied consistently, an employee creation point that reads a file, and money arithmetic that cannot lose a penny.
3. User requirements¶
- [ ] Register employees of the four kinds, with the data each kind needs.
- [ ] Run a month's payroll and print a payslip for each employee.
- [ ] See the gross total, the deduction total and the net total.
- [ ] Change an employee's rate without changing their kind.
- [ ] Load employees from a file, and be told the line number of a bad record.
- [ ] Reconcile: the printed total equals the sum of the individual net pays, exactly.
4. Functional requirements¶
- [ ] An abstract
Employeewithname(),grossPay(period),payKind()and a clone or a creation function. - [ ] Four concrete kinds, with at least one sharing behaviour with another through a base intermediate class or through composition — your choice, justified.
- [ ] A
Moneyvalue type in integer minor units, with+,-,*by a rate and<<. - [ ] Deduction rules applied by a separate component, not inside the employee types.
- [ ] A
Payrollowning employees, running the period and producing payslips. - [ ] A creation point reading a file: kind, name, rates, hours.
- [ ] Errors for an unknown kind, a negative rate, and a malformed record.
5. Non-functional requirements¶
- [ ] All money arithmetic in integers; conversion only at display.
- [ ] No rounding discrepancy between a payslip and the total — the reconciliation test proves it.
- [ ] Pay computation is free of side effects: running a period twice gives the same numbers.
- [ ] Adding a fifth employee kind touches one new file plus the creation point.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- Overtime at an increased rate above a threshold, with a documented threshold.
- Year-to-date totals per employee, held outside the employee object.
- A
PayRunvalue object that owns the payslips for one period, and a report comparing two runs. - A template
Repository<T>for employees and for payslips, if it genuinely removes duplication.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Employee + four kinds, maybe an intermediate kind, Money, deduction rules, Payroll, Payslip |
| Relationships | yes | Payroll ◆ employees; payslip ──> employee; deductions injected into the run |
| Encapsulation | yes | Money is always valid; rates cannot be negative; hours cannot be negative |
| Inheritance or composition | yes | At least one kind reuses another's rule — and you must say whether by inheritance or by composition, and why |
| Polymorphism | yes | grossPay dispatches per kind; the run loop contains no kind checks |
| The STL | yes | std::vector of employees, std::map for kind counts, std::accumulate for totals |
| Exception handling | yes | Unknown kind, bad rate and malformed record throw distinct types |
8. Suggested design considerations¶
The fourth kind is the design test. Hourly-plus-commission shares behaviour with two others.
Inherit from one and reimplement the other? Inherit from both and meet the diamond? Or compose a
PayRule from two parts? There is a good answer and a bad answer, and the reasoning is what is
marked.
Deductions do not belong in the employee. An employee knows what they earn; the payroll knows what the state takes. Keeping them apart is what lets a tax-table change touch one file.
Money as a type pays for itself immediately. Once Money exists with a correct operator+
and a documented rounding rule for multiplication by a rate, the reconciliation test passes or
fails for real reasons rather than for floating-point ones.
Reconcile in the test, not by eye. Sum the payslips and compare with the printed total as integers. That test is the one the firm actually needs.
9. Minimum viable version¶
Money, two employee kinds, a Payroll that runs a period and prints payslips and a total.
Creation by hand in main.
10. Intermediate extension¶
Add the other two kinds, including the combined one, plus the deduction component and the reconciliation test. Then add a third deduction rule and count the files it touched.
11. Advanced extension¶
Add file-driven employee creation, a PayRun record of a period, and year-to-date totals held
outside the employees. Then add a fifth kind and verify the change cost you predicted; if it
took more files than predicted, say which assumption was wrong.
12. Testing checklist¶
- [ ] A salaried employee's monthly gross is exactly the annual figure divided by twelve, per the documented rounding rule
- [ ] An hourly employee's gross uses the hours given, including zero
- [ ] Commission-only pay on zero sales is zero, not negative
- [ ] The combined kind's pay equals the sum of its two parts, to the penny
- [ ] The gross total equals the sum of the individual gross pays, exactly
- [ ] Net pay equals gross minus deductions for every employee
- [ ] Running the same period twice produces identical numbers
- [ ] A negative rate is refused, and the employee is not created
- [ ] A record with an unknown kind is refused with its line number
- [ ] Adding a fifth kind touches only the new file and the creation point
- [ ] Multiplication by a fractional rate rounds exactly as documented (test the half-unit case)
13. Documentation checklist¶
- [ ] The pay rules for each kind, as a specification, with a worked example each
- [ ] The money type: representation, rounding rule, and why integers
- [ ] The class diagram, with the reuse relationship shown and justified in one sentence
- [ ] The employee file format
- [ ] The reconciliation evidence, pasted from a real run
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Pay-rule design | Kinds polymorphic; the fourth kind's reuse decided and defended | 25% |
| Money correctness | Integer arithmetic; rounding documented and tested at the boundary | 25% |
| Reconciliation | Totals equal the sum of the parts, demonstrated | 15% |
| Separation | Deductions and year-to-date data outside the employee types | 15% |
| Failure handling | Bad rates, kinds and records all refused with context | 10% |
| Documentation | Rules, representation and evidence written down | 10% |
Related: Intermediate projects · Project roadmap · All projects · Module 6 — Inheritance