I work on DB2, but I took a quick look at an Oracle manual, and it looks pretty close to DB2 in this respect. This query should get you pretty close to the finish line, although it exposes 3 result columns, where Sum_Earnings represents either a department total, person total, or grand total for all departments, depending on the sum record type (the GROUPING function is used to determine which level of total row you're dealing with). With minor tweaking you should be able to turn this into exposing 4 result columns if you really need it (hint: grouping function).
select decode( grouping( D.DepartmentName ), 1, 'All Departments', D.DepartmentName )
as Department
,decode( grouping( P.Surname ), 1, 'All Persons', P.Surname ) as Person_Surname
,sum( E.EarningValue ) as Sum_Earnings
from Departments D
join People P
on P.DepartmentID = D.DepartmentID
join Earnings E
on E.PersonID = P.PersonID
group by rollup( D.DepartmentName, P.Surname )
order by grouping( D.DepartmentName ), Department, grouping( P.Surname ), Person_Surname
Because CUBE, ROLLUP, and GROUPING SETS can perform these tasks with a much smaller volume of code, they are really the way to go as there is less chance of making a coding mistake (once you wrap you head around them), and if you're producing a summary report like this, by allowing the database to perform all the summation work, you're likely to get much better performance (as opposed to reading detail rows into a program which calculates totals by churning detail rows - ugh).
It usually takes me a couple tries to get the row ordering and total labeling correct on these types of queries as I don't do them often (rarely work on reporting in recent years), so hopefully I didn't hose that up.