From cda6cfb4cdbda41a188c5bd4cf137a512dec9376 Mon Sep 17 00:00:00 2001 From: James Cole Date: Mon, 9 May 2016 20:15:26 +0200 Subject: [PATCH] Fix more charts. --- .../Controllers/Chart/CategoryController.php | 518 +++++++++--------- app/Models/Category.php | 2 +- .../Category/CategoryRepository.php | 31 +- .../Category/CategoryRepositoryInterface.php | 9 + 4 files changed, 303 insertions(+), 257 deletions(-) diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index b7c974efab..cd1a09c2ea 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -8,10 +8,10 @@ use Carbon\Carbon; use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; -use FireflyIII\Repositories\Account\AccountRepositoryInterface as ARI; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; +use Log; use Navigation; use Preferences; use Response; @@ -45,37 +45,34 @@ class CategoryController extends Controller /** * Show an overview for a category for all time, per month/week/year. * - * @param CRI $repository + * @param CRI $repository * @param Category $category * * @return \Symfony\Component\HttpFoundation\Response */ public function all(CRI $repository, Category $category) { - /** - // oldest transaction in category: - $start = $repository->getFirstActivityDate($category); - $range = Preferences::get('viewRange', '1M')->data; - $start = Navigation::startOfPeriod($start, $range); - $end = new Carbon; - $entries = new Collection; - // chart properties for cache: - $cache = new CacheProperties(); + $start = $repository->firstUseDate($category, new Collection); + $range = Preferences::get('viewRange', '1M')->data; + $start = Navigation::startOfPeriod($start, $range); + $categoryCollection = new Collection([$category]); + $end = new Carbon; + $entries = new Collection; + $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty('all'); $cache->addProperty('categories'); if ($cache->has()) { - return Response::json($cache->get()); + //return Response::json($cache->get()); } - $spentArray = $repository->spentPerDay($category, $start, $end, new Collection); - $earnedArray = $repository->earnedPerDay($category, $start, $end, new Collection); while ($start <= $end) { $currentEnd = Navigation::endOfPeriod($start, $range); - $spent = $this->getSumOfRange($start, $currentEnd, $spentArray); - $earned = $this->getSumOfRange($start, $currentEnd, $earnedArray); - $date = Navigation::periodShow($start, $range); + Log::debug('Searching for expenses from ' . $start . ' to ' . $currentEnd); + $spent = $repository->spentInPeriod($categoryCollection, new Collection, $start, $currentEnd); + $earned = $repository->earnedInPeriod($categoryCollection, new Collection, $start, $currentEnd); + $date = Navigation::periodShow($start, $range); $entries->push([clone $start, $date, $spent, $earned]); $start = Navigation::addPeriod($start, $range, 0); } @@ -85,25 +82,25 @@ class CategoryController extends Controller $data = $this->generator->all($entries); $cache->store($data); + return ' ' . json_encode($data); + return Response::json($data); - * **/ + } /** - * @param CRI $repository + * @param CRI $repository * @param Category $category * * @return \Symfony\Component\HttpFoundation\Response */ public function currentPeriod(CRI $repository, Category $category) { - /** $start = clone session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); $data = $this->makePeriodChart($repository, $category, $start, $end); return Response::json($data); - * **/ } /** @@ -121,29 +118,29 @@ class CategoryController extends Controller public function earnedInPeriod(CRI $repository, string $reportType, Carbon $start, Carbon $end, Collection $accounts) { /** - $cache = new CacheProperties; // chart properties for cache: - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($reportType); - $cache->addProperty($accounts); - $cache->addProperty('category'); - $cache->addProperty('earned-in-period'); - if ($cache->has()) { - return Response::json($cache->get()); - } - - $set = $repository->earnedForAccountsPerMonth($accounts, $start, $end); - $categories = $set->unique('id')->sortBy( - function (Category $category) { - return $category->name; - } - ); - $entries = $this->filterCollection($start, $end, $set, $categories); - $data = $this->generator->earnedInPeriod($categories, $entries); - $cache->store($data); - - return $data; - **/ + * $cache = new CacheProperties; // chart properties for cache: + * $cache->addProperty($start); + * $cache->addProperty($end); + * $cache->addProperty($reportType); + * $cache->addProperty($accounts); + * $cache->addProperty('category'); + * $cache->addProperty('earned-in-period'); + * if ($cache->has()) { + * return Response::json($cache->get()); + * } + * + * $set = $repository->earnedForAccountsPerMonth($accounts, $start, $end); + * $categories = $set->unique('id')->sortBy( + * function (Category $category) { + * return $category->name; + * } + * ); + * $entries = $this->filterCollection($start, $end, $set, $categories); + * $data = $this->generator->earnedInPeriod($categories, $entries); + * $cache->store($data); + * + * return $data; + **/ } /** @@ -151,16 +148,12 @@ class CategoryController extends Controller * * @param CRI $repository * - * @param ARI $accountRepository - * * @return \Symfony\Component\HttpFoundation\Response */ - public function frontpage(CRI $repository, ARI $accountRepository) + public function frontpage(CRI $repository) { - /** $start = session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); - // chart properties for cache: $cache = new CacheProperties; $cache->addProperty($start); @@ -168,18 +161,23 @@ class CategoryController extends Controller $cache->addProperty('category'); $cache->addProperty('frontpage'); if ($cache->has()) { - return Response::json($cache->get()); + //return Response::json($cache->get()); + } + $categories = $repository->getCategories(); + $set = new Collection; + /** @var Category $category */ + foreach ($categories as $category) { + $spent = $repository->spentInPeriod(new Collection([$category]), new Collection, $start, $end); + Log::debug('Spent for ' . $category->name . ' is ' . $spent . ' (' . bccomp($spent, '0') . ')'); + if (bccomp($spent, '0') === -1) { + $category->spent = $spent; + $set->push($category); + } } - - // get data for categories (and "no category"): - $accounts = $accountRepository->getAccounts(['Default account', 'Asset account', 'Cash account']); - $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); - $outside = $repository->sumSpentNoCategory($accounts, $start, $end); - // this is a "fake" entry for the "no category" entry. - $entry = new stdClass(); + $entry = new stdClass; $entry->name = trans('firefly.no_category'); - $entry->spent = $outside; + $entry->spent = $repository->spentInPeriodWithoutCategory(new Collection, $start, $end); $set->push($entry); $set = $set->sortBy('spent'); @@ -187,7 +185,7 @@ class CategoryController extends Controller $cache->store($data); return Response::json($data); - **/ + } /** @@ -202,76 +200,76 @@ class CategoryController extends Controller public function multiYear(string $reportType, Carbon $start, Carbon $end, Collection $accounts, Collection $categories) { /** - // /** @var CRI $repository - // $repository = app(CRI::class); - - // chart properties for cache: - $cache = new CacheProperties(); - $cache->addProperty($reportType); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($accounts); - $cache->addProperty($categories); - $cache->addProperty('multiYearCategory'); - - if ($cache->has()) { - return Response::json($cache->get()); - } - - $entries = new Collection; - $set = $repository->listMultiYear($categories, $accounts, $start, $end); - - /** @var Category $category - foreach ($categories as $category) { - $entry = ['name' => '', 'spent' => [], 'earned' => []]; - - $currentStart = clone $start; - while ($currentStart < $end) { - // fix the date: - $year = $currentStart->year; - $currentEnd = clone $currentStart; - $currentEnd->endOfYear(); - - - // get data: - if (is_null($category->id)) { - $name = trans('firefly.noCategory'); - $spent = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd); - $earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd); - } else { - // get from set: - $entrySpent = $set->filter( - function (Category $cat) use ($year, $category) { - return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id); - } - )->first(); - $entryEarned = $set->filter( - function (Category $cat) use ($year, $category) { - return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id); - } - )->first(); - - $name = $category->name; - $spent = !is_null($entrySpent) ? $entrySpent->sum : 0; - $earned = !is_null($entryEarned) ? $entryEarned->sum : 0; - } - - // save to array: - $entry['name'] = $name; - $entry['spent'][$year] = ($spent * -1); - $entry['earned'][$year] = $earned; - - // jump to next year. - $currentStart = clone $currentEnd; - $currentStart->addDay(); - } - $entries->push($entry); - } - // generate chart with data: - $data = $this->generator->multiYear($entries); - $cache->store($data); - - return Response::json($data); + * // /** @var CRI $repository + * // $repository = app(CRI::class); + * + * // chart properties for cache: + * $cache = new CacheProperties(); + * $cache->addProperty($reportType); + * $cache->addProperty($start); + * $cache->addProperty($end); + * $cache->addProperty($accounts); + * $cache->addProperty($categories); + * $cache->addProperty('multiYearCategory'); + * + * if ($cache->has()) { + * return Response::json($cache->get()); + * } + * + * $entries = new Collection; + * $set = $repository->listMultiYear($categories, $accounts, $start, $end); + * + * /** @var Category $category + * foreach ($categories as $category) { + * $entry = ['name' => '', 'spent' => [], 'earned' => []]; + * + * $currentStart = clone $start; + * while ($currentStart < $end) { + * // fix the date: + * $year = $currentStart->year; + * $currentEnd = clone $currentStart; + * $currentEnd->endOfYear(); + * + * + * // get data: + * if (is_null($category->id)) { + * $name = trans('firefly.noCategory'); + * $spent = $repository->sumSpentNoCategory($accounts, $currentStart, $currentEnd); + * $earned = $repository->sumEarnedNoCategory($accounts, $currentStart, $currentEnd); + * } else { + * // get from set: + * $entrySpent = $set->filter( + * function (Category $cat) use ($year, $category) { + * return ($cat->type == 'Withdrawal' && $cat->dateFormatted == $year && $cat->id == $category->id); + * } + * )->first(); + * $entryEarned = $set->filter( + * function (Category $cat) use ($year, $category) { + * return ($cat->type == 'Deposit' && $cat->dateFormatted == $year && $cat->id == $category->id); + * } + * )->first(); + * + * $name = $category->name; + * $spent = !is_null($entrySpent) ? $entrySpent->sum : 0; + * $earned = !is_null($entryEarned) ? $entryEarned->sum : 0; + * } + * + * // save to array: + * $entry['name'] = $name; + * $entry['spent'][$year] = ($spent * -1); + * $entry['earned'][$year] = $earned; + * + * // jump to next year. + * $currentStart = clone $currentEnd; + * $currentStart->addDay(); + * } + * $entries->push($entry); + * } + * // generate chart with data: + * $data = $this->generator->multiYear($entries); + * $cache->store($data); + * + * return Response::json($data); * */ @@ -289,54 +287,54 @@ class CategoryController extends Controller public function period(Category $category, string $reportType, Carbon $start, Carbon $end, Collection $accounts) { /** - // chart properties for cache: - $cache = new CacheProperties(); - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($reportType); - $cache->addProperty($accounts); - $cache->addProperty($category->id); - $cache->addProperty('category'); - $cache->addProperty('period'); - if ($cache->has()) { - return Response::json($cache->get()); - } - - /** @var CategoryRepositoryInterface $repository - $repository = app(CategoryRepositoryInterface::class); - // loop over period, add by users range: - $current = clone $start; - $viewRange = Preferences::get('viewRange', '1M')->data; - $format = strval(trans('config.month')); - $set = new Collection; - while ($current < $end) { - $currentStart = clone $current; - $currentEnd = Navigation::endOfPeriod($currentStart, $viewRange); - - $spent = strval(array_sum($repository->spentPerDay($category, $currentStart, $currentEnd, $accounts))); - $earned = strval(array_sum($repository->earnedPerDay($category, $currentStart, $currentEnd, $accounts))); - - $entry = [ - $category->name, - $currentStart->formatLocalized($format), - $spent, - $earned, - - ]; - $set->push($entry); - $currentEnd->addDay(); - $current = clone $currentEnd; - } - $data = $this->generator->period($set); - $cache->store($data); - - return Response::json($data); + * // chart properties for cache: + * $cache = new CacheProperties(); + * $cache->addProperty($start); + * $cache->addProperty($end); + * $cache->addProperty($reportType); + * $cache->addProperty($accounts); + * $cache->addProperty($category->id); + * $cache->addProperty('category'); + * $cache->addProperty('period'); + * if ($cache->has()) { + * return Response::json($cache->get()); + * } + * + * /** @var CategoryRepositoryInterface $repository + * $repository = app(CategoryRepositoryInterface::class); + * // loop over period, add by users range: + * $current = clone $start; + * $viewRange = Preferences::get('viewRange', '1M')->data; + * $format = strval(trans('config.month')); + * $set = new Collection; + * while ($current < $end) { + * $currentStart = clone $current; + * $currentEnd = Navigation::endOfPeriod($currentStart, $viewRange); + * + * $spent = strval(array_sum($repository->spentPerDay($category, $currentStart, $currentEnd, $accounts))); + * $earned = strval(array_sum($repository->earnedPerDay($category, $currentStart, $currentEnd, $accounts))); + * + * $entry = [ + * $category->name, + * $currentStart->formatLocalized($format), + * $spent, + * $earned, + * + * ]; + * $set->push($entry); + * $currentEnd->addDay(); + * $current = clone $currentEnd; + * } + * $data = $this->generator->period($set); + * $cache->store($data); + * + * return Response::json($data); * **/ } /** - * @param CRI $repository + * @param CRI $repository * @param Category $category * * @param $date @@ -345,7 +343,6 @@ class CategoryController extends Controller */ public function specificPeriod(CRI $repository, Category $category, $date) { - /** $carbon = new Carbon($date); $range = Preferences::get('viewRange', '1M')->data; $start = Navigation::startOfPeriod($carbon, $range); @@ -353,8 +350,6 @@ class CategoryController extends Controller $data = $this->makePeriodChart($repository, $category, $start, $end); return Response::json($data); - - **/ } /** @@ -373,30 +368,30 @@ class CategoryController extends Controller public function spentInPeriod(CRI $repository, $reportType, Carbon $start, Carbon $end, Collection $accounts) { /** - $cache = new CacheProperties; // chart properties for cache: - $cache->addProperty($start); - $cache->addProperty($end); - $cache->addProperty($reportType); - $cache->addProperty($accounts); - $cache->addProperty('category'); - $cache->addProperty('spent-in-period'); - if ($cache->has()) { - return Response::json($cache->get()); - } - - - $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); - $categories = $set->unique('id')->sortBy( - function (Category $category) { - return $category->name; - } - ); - $entries = $this->filterCollection($start, $end, $set, $categories); - $entries = $this->invertSelection($entries); - $data = $this->generator->spentInPeriod($categories, $entries); - $cache->store($data); - - return $data; + * $cache = new CacheProperties; // chart properties for cache: + * $cache->addProperty($start); + * $cache->addProperty($end); + * $cache->addProperty($reportType); + * $cache->addProperty($accounts); + * $cache->addProperty('category'); + * $cache->addProperty('spent-in-period'); + * if ($cache->has()) { + * return Response::json($cache->get()); + * } + * + * + * $set = $repository->spentForAccountsPerMonth($accounts, $start, $end); + * $categories = $set->unique('id')->sortBy( + * function (Category $category) { + * return $category->name; + * } + * ); + * $entries = $this->filterCollection($start, $end, $set, $categories); + * $entries = $this->invertSelection($entries); + * $data = $this->generator->spentInPeriod($categories, $entries); + * $cache->store($data); + * + * return $data; * */ } @@ -411,33 +406,33 @@ class CategoryController extends Controller private function filterCollection(Carbon $start, Carbon $end, Collection $set, Collection $categories): Collection { /** - $entries = new Collection; - - while ($start < $end) { // filter the set: - $row = [clone $start]; - $currentSet = $set->filter( // get possibly relevant entries from the big $set - function (Category $category) use ($start) { - return $category->dateFormatted == $start->format('Y-m'); - } - ); - /** @var Category $category - foreach ($categories as $category) { // check for each category if its in the current set. - $entry = $currentSet->filter( // if its in there, use the value. - function (Category $cat) use ($category) { - return ($cat->id == $category->id); - } - )->first(); - if (!is_null($entry)) { - $row[] = $entry->earned ? round($entry->earned, 2) : round($entry->spent, 2); - } else { - $row[] = 0; - } - } - $entries->push($row); - $start->addMonth(); - } - - return $entries; + * $entries = new Collection; + * + * while ($start < $end) { // filter the set: + * $row = [clone $start]; + * $currentSet = $set->filter( // get possibly relevant entries from the big $set + * function (Category $category) use ($start) { + * return $category->dateFormatted == $start->format('Y-m'); + * } + * ); + * /** @var Category $category + * foreach ($categories as $category) { // check for each category if its in the current set. + * $entry = $currentSet->filter( // if its in there, use the value. + * function (Category $cat) use ($category) { + * return ($cat->id == $category->id); + * } + * )->first(); + * if (!is_null($entry)) { + * $row[] = $entry->earned ? round($entry->earned, 2) : round($entry->spent, 2); + * } else { + * $row[] = 0; + * } + * } + * $entries->push($row); + * $start->addMonth(); + * } + * + * return $entries; * */ } @@ -451,23 +446,23 @@ class CategoryController extends Controller private function invertSelection(Collection $entries): Collection { /** - $result = new Collection; - foreach ($entries as $entry) { - $new = [$entry[0]]; - $count = count($entry); - for ($i = 1; $i < $count; $i++) { - $new[$i] = ($entry[$i] * -1); - } - $result->push($new); - } - - return $result; + * $result = new Collection; + * foreach ($entries as $entry) { + * $new = [$entry[0]]; + * $count = count($entry); + * for ($i = 1; $i < $count; $i++) { + * $new[$i] = ($entry[$i] * -1); + * } + * $result->push($new); + * } + * + * return $result; * **/ } /** - * @param CRI $repository + * @param CRI $repository * @param Category $category * @param Carbon $start * @param Carbon $end @@ -476,28 +471,25 @@ class CategoryController extends Controller */ private function makePeriodChart(CRI $repository, Category $category, Carbon $start, Carbon $end) { - /** - // chart properties for cache: - $cache = new CacheProperties; + $categoryCollection = new Collection([$category]); + $cache = new CacheProperties; $cache->addProperty($start); $cache->addProperty($end); $cache->addProperty($category->id); $cache->addProperty('specific-period'); + if ($cache->has()) { - return $cache->get(); // + // return $cache->get(); } $entries = new Collection; - - // get amount earned in period, grouped by day. - // get amount spent in period, grouped by day. - $spentArray = $repository->spentPerDay($category, $start, $end, new Collection); - $earnedArray = $repository->earnedPerDay($category, $start, $end, new Collection); - + Log::debug('Start is ' . $start . ' en end is ' . $end); while ($start <= $end) { - $str = $start->format('Y-m-d'); - $spent = $spentArray[$str] ?? '0'; - $earned = $earnedArray[$str] ?? '0'; - $date = Navigation::periodShow($start, '1D'); + Log::debug('Now at ' . $start); + $spent = $repository->spentInPeriod($categoryCollection, new Collection, $start, $start); + Log::debug('spent: ' . $spent); + $earned = $repository->earnedInPeriod($categoryCollection, new Collection, $start, $start); + Log::debug('earned: ' . $earned); + $date = Navigation::periodShow($start, '1D'); $entries->push([clone $start, $date, $spent, $earned]); $start->addDay(); } @@ -506,7 +498,27 @@ class CategoryController extends Controller $cache->store($data); return $data; - */ + + /** + * // get amount earned in period, grouped by day. + * // get amount spent in period, grouped by day. + * $spentArray = $repository->spentPerDay($category, $start, $end, new Collection); + * $earnedArray = $repository->earnedPerDay($category, $start, $end, new Collection); + * + * while ($start <= $end) { + * $str = $start->format('Y-m-d'); + * $spent = $spentArray[$str] ?? '0'; + * $earned = $earnedArray[$str] ?? '0'; + * $date = Navigation::periodShow($start, '1D'); + * $entries->push([clone $start, $date, $spent, $earned]); + * $start->addDay(); + * } + * + * $data = $this->generator->period($entries); + * $cache->store($data); + * + * return $data; + */ } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 325ba87e39..cc7a881a47 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -20,7 +20,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property-read \Illuminate\Database\Eloquent\Collection|TransactionJournal[] $transactionjournals * @property-read \FireflyIII\User $user * @property string $dateFormatted - * @property float $spent + * @property string $spent * @property \Carbon\Carbon $lastActivity * @property string $type * @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Category whereId($value) diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 613f990aca..192dfbf5bf 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -378,7 +378,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; $journals = $this->journalsInPeriod($categories, $accounts, $types, $start, $end); $sum = '0'; foreach ($journals as $journal) { @@ -652,6 +652,13 @@ class CategoryRepository implements CategoryRepositoryInterface $complete = $complete->merge($first); $complete = $complete->merge($second); + // sort: + $complete = $complete->sortByDesc( + function (TransactionJournal $journal) { + return $journal->date->format('Ymd'); + } + ); + // create paginator $offset = ($page - 1) * $pageSize; $subSet = $complete->slice($offset, $pageSize); @@ -677,7 +684,7 @@ class CategoryRepository implements CategoryRepositoryInterface // first collect actual transaction journals (fairly easy) $query = $this->user->transactionjournals()->expanded(); - if ($end > $start) { + if ($end >= $start) { $query->before($end)->after($start); } @@ -833,7 +840,7 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { - $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; $journals = $this->journalsInPeriod($categories, $accounts, $types, $start, $end); $sum = '0'; foreach ($journals as $journal) { @@ -843,6 +850,24 @@ class CategoryRepository implements CategoryRepositoryInterface return $sum; } + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string + { + $journals = $this->journalsInPeriodWithoutCategory($accounts, $start, $end); + $sum = '0'; + foreach ($journals as $journal) { + $sum = bcadd(TransactionJournal::amount($journal), $sum); + } + + return $sum; + } + /** * @param array $data * diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 9cf56f4139..b561dd2060 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -188,6 +188,15 @@ interface CategoryRepositoryInterface */ public function journalsInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): Collection; + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string; + /** * @param Collection $accounts * @param Carbon $start