I like to start with account types:

and chart of accounts

of course utilities can be separated.
then an expense or income can be entered:

I can do reports:

Something like this in laravel:

code for report
public function monthlyReport()
{
$bdate = Request::input('begindate');
$edate = Request::input('enddate');
$sql = "select distinct `account_types`.`AccountType` AS `AccountType`,`accounts`.`AccountNumber` AS `AccountNumber`,`accounts`.`AccountName` AS `AccountName`,sum(`transactions`.`Expense`) AS `Sum_Expense`,sum(`transactions`.`Income`) AS `Sum_Income` from ((`account_types` join `accounts` on((`account_types`.`AccountTypeID` = `accounts`.`AccountTypeID`))) join `transactions` on((`accounts`.`AccountID` = `transactions`.`AccountID`))) where (`transactions`.`TransactionDate` Between '$bdate' and '$edate') group by `account_types`.`AccountType`,`accounts`.`AccountNumber`,`accounts`.`AccountName`";
$sth = DB::getPdo()->prepare($sql);
$sth->execute();
$quy = $sth->fetchAll(\PDO::FETCH_OBJ);
$title = 'Monthly Report';
return view('account.report', compact('quy', 'title'));
}
Did not worry about binding dates, but for binding, and do bind, looks like:
where (`transactions`.`TransactionDate` Between :bdate and :edate)
and
$params = ['bdate' => $bdate, 'edate' => $edate];
$sth->execute($params);
Normally date and numeric are safe anyway.
An example db facade:
https://laracasts.com/discuss/channels/laravel/sql-native-to-query-builder
Of course there are detailed reports as well, but usually a monthly summary is all board of directors need in this case.
And a "flag" for as example not paid rent you could do a monthly report on. Usually done by date in this case.
So a calc of "X number of days past due" could be the flag.
Of course getPdo is just what I like, you can also design eloquent queries:
Just example here:
$quy = Powner::query()->leftJoin('dc_pets', 'dc_powners.ownerid', '=', 'dc_pets.ownerid')
->select('dc_powners.ownerid', 'dc_powners.oname')
->selectRaw('count(dc_pets.petid) as countOfPets')
->groupby('dc_powners.ownerid')
->orderby('dc_powners.oname')
->get();
Results basically give:
ownerid, oname, countOfPets
Like:
5|Bob|3
4|Greg|9
2|Rob|1
Just me, but I like the orm for maybe a one to many, but usually for larger queries, I prefer normal sql and pdo.
And usually in accounting specifically bookkeeping, you will need aggregates in many queries involving reports and summary data.