diff --git a/app/Api/V1/Controllers/Chart/CategoryController.php b/app/Api/V1/Controllers/Chart/CategoryController.php index dbe49283bf..63b0b17975 100644 --- a/app/Api/V1/Controllers/Chart/CategoryController.php +++ b/app/Api/V1/Controllers/Chart/CategoryController.php @@ -28,6 +28,8 @@ use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\DateRequest; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -40,8 +42,15 @@ class CategoryController extends Controller /** @var CategoryRepositoryInterface */ private $categoryRepository; + /** @var OperationsRepositoryInterface */ + private $opsRepository; + + /** @var NoCategoryRepositoryInterface */ + private $noCatRepository; + /** * AccountController constructor. + * * @codeCoverageIgnore */ public function __construct() @@ -52,7 +61,11 @@ class CategoryController extends Controller /** @var User $user */ $user = auth()->user(); $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->noCatRepository = app(NoCategoryRepositoryInterface::class); $this->categoryRepository->setUser($user); + $this->opsRepository->setUser($user); + $this->noCatRepository->setUser($user); return $next($request); } @@ -78,8 +91,8 @@ class CategoryController extends Controller $tempData = []; - $spent = $this->categoryRepository->spentInPeriodPerCurrency(new Collection, new Collection, $start, $end); - $earned = $this->categoryRepository->earnedInPeriodPerCurrency(new Collection, new Collection, $start, $end); + $spent = $this->opsRepository->spentInPeriodPerCurrency(new Collection, new Collection, $start, $end); + $earned = $this->opsRepository->earnedInPeriodPerCurrency(new Collection, new Collection, $start, $end); $categories = []; // earned: @@ -110,7 +123,7 @@ class CategoryController extends Controller } // earned with no category: - $noCategory = $this->categoryRepository->earnedInPeriodPcWoCategory(new Collection, $start, $end); + $noCategory = $this->noCatRepository->earnedInPeriodPcWoCategory(new Collection, $start, $end); foreach ($noCategory as $currencyId => $income) { $categoryName = (string)trans('firefly.no_category'); // find or make set for currency: @@ -164,7 +177,9 @@ class CategoryController extends Controller } // spent with no category - $noCategory = $this->categoryRepository->spentInPeriodPcWoCategory(new Collection, $start, $end); + $noCategory = $this->noCatRepository->spentInPeriodPcWoCategory(new Collection, $start, $end); + + foreach ($noCategory as $currencyId => $expense) { $categoryName = (string)trans('firefly.no_category'); // find or make set for currency: diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 7c90d8ae22..d16dd1efd4 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -31,6 +31,8 @@ use FireflyIII\Models\Category; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Chart\Category\WholePeriodChartGenerator; @@ -52,6 +54,7 @@ class CategoryController extends Controller /** * CategoryController constructor. + * * @codeCoverageIgnore */ public function __construct() @@ -81,10 +84,10 @@ class CategoryController extends Controller if ($cache->has()) { return response()->json($cache->get()); // @codeCoverageIgnore } - $start = $repository->firstUseDate($category) ?? $this->getDate(); - $range = app('preferences')->get('viewRange', '1M')->data; - $start = app('navigation')->startOfPeriod($start, $range); - $end = $this->getDate(); + $start = $repository->firstUseDate($category) ?? $this->getDate(); + $range = app('preferences')->get('viewRange', '1M')->data; + $start = app('navigation')->startOfPeriod($start, $range); + $end = $this->getDate(); Log::debug(sprintf('Full range is %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); @@ -101,12 +104,9 @@ class CategoryController extends Controller /** * Shows the category chart on the front page. * - * @param CategoryRepositoryInterface $repository - * @param AccountRepositoryInterface $accountRepository - * * @return JsonResponse */ - public function frontPage(CategoryRepositoryInterface $repository, AccountRepositoryInterface $accountRepository): JsonResponse + public function frontPage(): JsonResponse { $start = session('start', Carbon::now()->startOfMonth()); $end = session('end', Carbon::now()->endOfMonth()); @@ -122,7 +122,19 @@ class CategoryController extends Controller // currency repos: /** @var CurrencyRepositoryInterface $currencyRepository */ $currencyRepository = app(CurrencyRepositoryInterface::class); - $currencies = []; + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + + /** @var AccountRepositoryInterface $accountRepository */ + $accountRepository = app(AccountRepositoryInterface::class); + + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + + /** @var NoCategoryRepositoryInterface $noCatRepository */ + $noCatRepository = app(NoCategoryRepositoryInterface::class); + + $currencies = []; $chartData = []; @@ -132,7 +144,7 @@ class CategoryController extends Controller /** @var Category $category */ foreach ($categories as $category) { - $spentArray = $repository->spentInPeriodPerCurrency(new Collection([$category]), $accounts, $start, $end); + $spentArray = $opsRepository->spentInPeriodPerCurrency(new Collection([$category]), $accounts, $start, $end); foreach ($spentArray as $categoryId => $spentInfo) { foreach ($spentInfo['spent'] as $currencyId => $row) { $spent = $row['spent']; @@ -150,7 +162,7 @@ class CategoryController extends Controller } // no category per currency: - $noCategory = $repository->spentInPeriodPcWoCategory(new Collection, $start, $end); + $noCategory = $noCatRepository->spentInPeriodPcWoCategory(new Collection, $start, $end); foreach ($noCategory as $currencyId => $spent) { $currencies[$currencyId] = $currencies[$currencyId] ?? $currencyRepository->findNull($currencyId); $tempData[] = [ @@ -212,13 +224,16 @@ class CategoryController extends Controller $cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($category); if ($cache->has()) { - return response()->json($cache->get());// @codeCoverageIgnore + return response()->json($cache->get());// @codeCoverageIgnore } - $repository = app(CategoryRepositoryInterface::class); - $expenses = $repository->periodExpenses(new Collection([$category]), $accounts, $start, $end); - $income = $repository->periodIncome(new Collection([$category]), $accounts, $start, $end); - $periods = app('navigation')->listOfPeriods($start, $end); - $chartData = [ + + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + + $expenses = $opsRepository->periodExpenses(new Collection([$category]), $accounts, $start, $end); + $income = $opsRepository->periodIncome(new Collection([$category]), $accounts, $start, $end); + $periods = app('navigation')->listOfPeriods($start, $end); + $chartData = [ [ 'label' => (string)trans('firefly.spent'), 'entries' => [], @@ -256,8 +271,6 @@ class CategoryController extends Controller } - - /** * Chart for period for transactions without a category. * @@ -279,11 +292,14 @@ class CategoryController extends Controller if ($cache->has()) { return response()->json($cache->get()); // @codeCoverageIgnore } - $repository = app(CategoryRepositoryInterface::class); - $expenses = $repository->periodExpensesNoCategory($accounts, $start, $end); - $income = $repository->periodIncomeNoCategory($accounts, $start, $end); - $periods = app('navigation')->listOfPeriods($start, $end); - $chartData = [ + + /** @var NoCategoryRepositoryInterface $noCatRepository */ + $noCatRepository = app(NoCategoryRepositoryInterface::class); + + $expenses = $noCatRepository->periodExpensesNoCategory($accounts, $start, $end); + $income = $noCatRepository->periodIncomeNoCategory($accounts, $start, $end); + $periods = app('navigation')->listOfPeriods($start, $end); + $chartData = [ [ 'label' => (string)trans('firefly.spent'), 'entries' => [], diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php index d018f5f3c6..8d8e5cbd1e 100644 --- a/app/Http/Controllers/Report/CategoryController.php +++ b/app/Http/Controllers/Report/CategoryController.php @@ -26,6 +26,8 @@ use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use FireflyIII\Support\CacheProperties; use FireflyIII\Support\Http\Controllers\BasicDataSupport; use Illuminate\Support\Collection; @@ -60,9 +62,13 @@ class CategoryController extends Controller } /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); + + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + $categories = $repository->getCategories(); - $data = $repository->periodExpenses($categories, $accounts, $start, $end); - $data[0] = $repository->periodExpensesNoCategory($accounts, $start, $end); + $data = $opsRepository->periodExpenses($categories, $accounts, $start, $end); + $data[0] = $opsRepository->periodExpensesNoCategory($accounts, $start, $end); $report = $this->filterPeriodReport($data); // depending on the carbon format (a reliable way to determine the general date difference) @@ -114,9 +120,16 @@ class CategoryController extends Controller } /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); + + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + + /** @var NoCategoryRepositoryInterface $noCatRepository */ + $noCatRepository = app(NoCategoryRepositoryInterface::class); + $categories = $repository->getCategories(); - $data = $repository->periodIncome($categories, $accounts, $start, $end); - $data[0] = $repository->periodIncomeNoCategory($accounts, $start, $end); + $data = $opsRepository->periodIncome($categories, $accounts, $start, $end); + $data[0] = $noCatRepository->periodIncomeNoCategory($accounts, $start, $end); $report = $this->filterPeriodReport($data); // depending on the carbon format (a reliable way to determine the general date difference) @@ -169,6 +182,10 @@ class CategoryController extends Controller /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); + + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + $categories = $repository->getCategories(); $report = [ 'categories' => [], @@ -176,8 +193,8 @@ class CategoryController extends Controller ]; /** @var Category $category */ foreach ($categories as $category) { - $spent = $repository->spentInPeriod($category, $accounts, $start, $end); - $earned = $repository->earnedInPeriod($category, $accounts, $start, $end); + $spent = $opsRepository->spentInPeriod($category, $accounts, $start, $end); + $earned = $opsRepository->earnedInPeriod($category, $accounts, $start, $end); if (0 === count($spent) && 0 === count($earned)) { continue; } diff --git a/app/Providers/CategoryServiceProvider.php b/app/Providers/CategoryServiceProvider.php index 7a3ab1ffff..7e10655a1d 100644 --- a/app/Providers/CategoryServiceProvider.php +++ b/app/Providers/CategoryServiceProvider.php @@ -24,6 +24,10 @@ namespace FireflyIII\Providers; use FireflyIII\Repositories\Category\CategoryRepository; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepository; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepository; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; @@ -47,7 +51,7 @@ class CategoryServiceProvider extends ServiceProvider { $this->app->bind( CategoryRepositoryInterface::class, - function (Application $app) { + static function (Application $app) { /** @var CategoryRepository $repository */ $repository = app(CategoryRepository::class); if ($app->auth->check()) { @@ -57,5 +61,31 @@ class CategoryServiceProvider extends ServiceProvider return $repository; } ); + + $this->app->bind( + OperationsRepositoryInterface::class, + static function (Application $app) { + /** @var OperationsRepository $repository */ + $repository = app(OperationsRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); + } + + return $repository; + } + ); + + $this->app->bind( + NoCategoryRepositoryInterface::class, + static function (Application $app) { + /** @var NoCategoryRepository $repository */ + $repository = app(NoCategoryRepository::class); + if ($app->auth->check()) { + $repository->setUser(auth()->user()); + } + + return $repository; + } + ); } } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index b901fcff5f..aa6cd8981e 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -24,22 +24,15 @@ namespace FireflyIII\Repositories\Category; use Carbon\Carbon; use FireflyIII\Factory\CategoryFactory; -use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Category; -use FireflyIII\Models\TransactionType; use FireflyIII\Services\Internal\Destroy\CategoryDestroyService; use FireflyIII\Services\Internal\Update\CategoryUpdateService; use FireflyIII\User; use Illuminate\Support\Collection; use Log; -use Navigation; /** * Class CategoryRepository. - * - * - * - * */ class CategoryRepository implements CategoryRepositoryInterface { @@ -73,175 +66,29 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end + * Find a category. * - * @return array + * @param string $name + * + * @return Category|null */ - public function earnedInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array + public function findByName(string $name): ?Category { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $categories = $this->user->categories()->get(['categories.*']); + // TODO no longer need to loop like this - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setCategory($category); - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - // collect and group results: - $array = $collector->getExtractedJournals(); - $return = []; - - foreach ($array as $journal) { - $currencyCode = $journal['currency_code']; - if (!isset($return[$currencyCode])) { - $return[$currencyCode] = [ - 'currency_id' => $journal['currency_id'], - 'currency_code' => $journal['currency_code'], - 'currency_name' => $journal['currency_name'], - 'currency_symbol' => $journal['currency_symbol'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - 'earned' => '0', - ]; + foreach ($categories as $category) { + if ($category->name === $name) { + return $category; } - - // also extract foreign currency information: - if (null !== $journal['foreign_currency_id']) { - $currencyCode = $journal['foreign_currency_code']; - if (!isset($return[$currencyCode])) { - $return[$currencyCode] = [ - 'currency_id' => $journal['foreign_currency_id'], - 'currency_code' => $journal['foreign_currency_code'], - 'currency_name' => $journal['foreign_currency_name'], - 'currency_symbol' => $journal['foreign_currency_symbol'], - 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], - 'earned' => '0', - ]; - } - $return[$currencyCode]['earned'] = bcadd($return[$currencyCode]['earned'], app('steam')->positive($journal['foreign_amount'])); - } - $return[$currencyCode]['earned'] = bcadd($return[$currencyCode]['earned'], app('steam')->positive($journal['amount'])); } - return $return; + return null; } /** - * A very cryptic method name that means: - * - * Get me the amount earned in this period, grouped per currency, where no category was set. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->withoutCategory(); - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - $journals = $collector->getExtractedJournals(); - $return = []; - - foreach ($journals as $journal) { - $currencyId = (int)$journal['currency_id']; - if (!isset($return[$currencyId])) { - $return[$currencyId] = [ - 'earned' => '0', - 'currency_id' => $currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - } - $return[$currencyId]['earned'] = bcadd($return[$currencyId]['earned'], $journal['amount']); - } - - return $return; - } - - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); - - if ($categories->count() > 0) { - $collector->setCategories($categories); - } - if (0 === $categories->count()) { - $collector->setCategories($this->getCategories()); - } - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - $journals = $collector->getExtractedJournals(); - $return = []; - foreach ($journals as $journal) { - $categoryId = (int)$journal['category_id']; - $currencyId = (int)$journal['currency_id']; - $name = $journal['category_name']; - // make array for category: - if (!isset($return[$categoryId])) { - $return[$categoryId] = [ - 'name' => $name, - 'earned' => [], - ]; - } - if (!isset($return[$categoryId]['earned'][$currencyId])) { - $return[$categoryId]['earned'][$currencyId] = [ - 'earned' => '0', - 'currency_id' => $currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - } - $return[$categoryId]['earned'][$currencyId]['earned'] - = bcadd($return[$categoryId]['earned'][$currencyId]['earned'], $journal['amount']); - } - - return $return; - } - - /** - * Returns a list of all the categories belonging to a user. - * - * @return Collection - */ - public function getCategories(): Collection - { - /** @var Collection $set */ - $set = $this->user->categories()->orderBy('name', 'ASC')->get(); - - return $set; - } - - /** - * @param int|null $categoryId + * @param int|null $categoryId * @param string|null $categoryName * * @return Category|null @@ -279,46 +126,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $this->user->categories()->find($categoryId); } - - - /** - * Find a category. - * - * @param string $name - * - * @return Category|null - */ - public function findByName(string $name): ?Category - { - $categories = $this->user->categories()->get(['categories.*']); - - // TODO no longer need to loop like this - - foreach ($categories as $category) { - if ($category->name === $name) { - return $category; - } - } - - return null; - } - - /** - * @param array $data - * - * @return Category - */ - public function store(array $data): Category - { - /** @var CategoryFactory $factory */ - $factory = app(CategoryFactory::class); - $factory->setUser($this->user); - - return $factory->findOrCreate(null, $data['name']); - } - - - /** * @param Category $category * @@ -360,7 +167,20 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Category $category + * Returns a list of all the categories belonging to a user. + * + * @return Collection + */ + public function getCategories(): Collection + { + /** @var Collection $set */ + $set = $this->user->categories()->orderBy('name', 'ASC')->get(); + + return $set; + } + + /** + * @param Category $category * @param Collection $accounts * * @return Carbon|null @@ -389,174 +209,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $lastJournalDate; } - - - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array - { - $carbonFormat = Navigation::preferredCarbonFormat($start, $end); - $data = []; - // prep data array: - /** @var Category $category */ - foreach ($categories as $category) { - $data[$category->id] = [ - 'name' => $category->name, - 'sum' => '0', - 'entries' => [], - ]; - } - - // get all transactions: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setAccounts($accounts)->setRange($start, $end); - $collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) - ->withAccountInformation()->withCategoryInformation(); - $journals = $collector->getExtractedJournals(); - - // loop transactions: - - foreach ($journals as $journal) { - $categoryId = (int)$journal['category_id']; - $date = $journal['date']->format($carbonFormat); - $data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $journal['amount']); - } - - return $data; - } - - - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array - { - $carbonFormat = Navigation::preferredCarbonFormat($start, $end); - - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation(); - $collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]); - $collector->withoutCategory(); - $journals = $collector->getExtractedJournals(); - $result = [ - 'entries' => [], - 'name' => (string)trans('firefly.no_category'), - 'sum' => '0', - ]; - - /** @var array $journal */ - foreach ($journals as $journal) { - $date = $journal['date']->format($carbonFormat); - - if (!isset($result['entries'][$date])) { - $result['entries'][$date] = '0'; - } - $result['entries'][$date] = bcadd($result['entries'][$date], $journal['amount']); - } - - return $result; - } - - - - /** - * TODO not multi currency aware. - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array - { - $carbonFormat = Navigation::preferredCarbonFormat($start, $end); - $data = []; - // prep data array: - /** @var Category $category */ - foreach ($categories as $category) { - $data[$category->id] = [ - 'name' => $category->name, - 'sum' => '0', - 'entries' => [], - ]; - } - - // get all transactions: - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setAccounts($accounts)->setRange($start, $end); - $collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) - ->withAccountInformation(); - $journals = $collector->getExtractedJournals(); - - // loop transactions: - /** @var array $journal */ - foreach ($journals as $journal) { - $categoryId = (int)$journal['category_id']; - $date = $journal['date']->format($carbonFormat); - $data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', bcmul($journal['amount'],'-1')); - } - - return $data; - } - - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array - { - Log::debug('Now in periodIncomeNoCategory()'); - $carbonFormat = Navigation::preferredCarbonFormat($start, $end); - - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation(); - $collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]); - $collector->withoutCategory(); - $journals = $collector->getExtractedJournals(); - $result = [ - 'entries' => [], - 'name' => (string)trans('firefly.no_category'), - 'sum' => '0', - ]; - Log::debug('Looping transactions..'); - - foreach ($journals as $journal) { - $date = $journal['date']->format($carbonFormat); - - if (!isset($result['entries'][$date])) { - $result['entries'][$date] = '0'; - } - $result['entries'][$date] = bcadd($result['entries'][$date], bcmul($journal['amount'],'-1')); - } - Log::debug('Done looping transactions..'); - Log::debug('Finished periodIncomeNoCategory()'); - - return $result; - } - /** * @param string $query * @@ -581,166 +233,22 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * Returns the amount spent in a category, for a set of accounts, in a specific period. + * @param array $data * - * @param Category $category - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array + * @return Category */ - public function spentInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array + public function store(array $data): Category { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategory($category); + /** @var CategoryFactory $factory */ + $factory = app(CategoryFactory::class); + $factory->setUser($this->user); - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - // collect and group results: - $array = $collector->getExtractedJournals(); - $return = []; - - foreach ($array as $journal) { - $currencyCode = $journal['currency_code']; - if (!isset($return[$currencyCode])) { - $return[$currencyCode] = [ - 'currency_id' => $journal['currency_id'], - 'currency_code' => $journal['currency_code'], - 'currency_name' => $journal['currency_name'], - 'currency_symbol' => $journal['currency_symbol'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - 'spent' => '0', - ]; - } - - // also extract foreign currency information: - if (null !== $journal['foreign_currency_id']) { - $currencyCode = $journal['foreign_currency_code']; - if (!isset($return[$currencyCode])) { - $return[$currencyCode] = [ - 'currency_id' => $journal['foreign_currency_id'], - 'currency_code' => $journal['foreign_currency_code'], - 'currency_name' => $journal['foreign_currency_name'], - 'currency_symbol' => $journal['foreign_currency_symbol'], - 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], - 'spent' => '0', - ]; - } - $return[$currencyCode]['spent'] = bcadd($return[$currencyCode]['spent'], $journal['foreign_amount']); - } - $return[$currencyCode]['spent'] = bcadd($return[$currencyCode]['spent'], $journal['amount']); - } - - return $return; - } - - /** - * A very cryptic method name that means: - * - * Get me the amount spent in this period, grouped per currency, where no category was set. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - - $set = $collector->getExtractedJournals(); - $return = []; - /** @var array $journal */ - foreach ($set as $journal) { - $currencyId = (int)$journal['currency_id']; - if (!isset($return[$currencyId])) { - $return[$currencyId] = [ - 'spent' => '0', - 'currency_id' => $currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - } - $return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $journal['amount']); - } - - return $return; - } - - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array - { - /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); - $collector->setUser($this->user); - $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); - - if ($categories->count() > 0) { - $collector->setCategories($categories); - } - if (0 === $categories->count()) { - $collector->setCategories($this->getCategories()); - } - - if ($accounts->count() > 0) { - $collector->setAccounts($accounts); - } - - $set = $collector->getExtractedJournals(); - $return = []; - /** @var array $journal */ - foreach ($set as $journal) { - $categoryId = (int)$journal['category_id']; - $currencyId = (int)$journal['currency_id']; - $name = $journal['category_name']; - - // make array for category: - if (!isset($return[$categoryId])) { - $return[$categoryId] = [ - 'name' => $name, - 'spent' => [], - ]; - } - if (!isset($return[$categoryId]['spent'][$currencyId])) { - $return[$categoryId]['spent'][$currencyId] = [ - 'spent' => '0', - 'currency_id' => $currencyId, - 'currency_symbol' => $journal['currency_symbol'], - 'currency_code' => $journal['currency_code'], - 'currency_decimal_places' => $journal['currency_decimal_places'], - ]; - } - $return[$categoryId]['spent'][$currencyId]['spent'] - = bcadd($return[$categoryId]['spent'][$currencyId]['spent'], $journal['amount']); - } - - return $return; + return $factory->findOrCreate(null, $data['name']); } /** * @param Category $category - * @param array $data + * @param array $data * * @return Category */ @@ -752,25 +260,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $service->update($category, $data); } - /** - * TODO does not take currencies into account. - * - * @param array $journals - * - * @return string - */ - private function sumJournals(array $journals): string - { - $sum = '0'; - /** @var array $journal */ - foreach ($journals as $journal) { - $amount = (string)$journal['amount']; - $sum = bcadd($sum, $amount); - } - - return $sum; - } - /** * @param Category $category * @@ -809,7 +298,7 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Category $category + * @param Category $category * @param Collection $accounts * * @return Carbon|null @@ -833,7 +322,7 @@ class CategoryRepository implements CategoryRepositoryInterface } /** - * @param Category $category + * @param Category $category * @param Collection $accounts * * @return Carbon|null @@ -857,4 +346,5 @@ class CategoryRepository implements CategoryRepositoryInterface return null; } + } diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 973e594e8b..4f3e33cd10 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -33,14 +33,6 @@ use Illuminate\Support\Collection; interface CategoryRepositoryInterface { - /** - * @param int|null $categoryId - * @param string|null $categoryName - * - * @return Category|null - */ - public function findCategory( ?int $categoryId, ?string $categoryName): ?Category; - /** * @param Category $category * @@ -48,41 +40,6 @@ interface CategoryRepositoryInterface */ public function destroy(Category $category): bool; - /** - * Returns the amount earned in a category, for a set of accounts, in a specific period. - * - * @param Category $category - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function earnedInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * A very cryptic method name that means: - * - * Get me the amount earned in this period, grouped per currency, where no category was set. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; - /** * Find a category. * @@ -92,6 +49,14 @@ interface CategoryRepositoryInterface */ public function findByName(string $name): ?Category; + /** + * @param int|null $categoryId + * @param string|null $categoryName + * + * @return Category|null + */ + public function findCategory(?int $categoryId, ?string $categoryName): ?Category; + /** * Find a category or return NULL * @@ -124,8 +89,6 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; - - /** * Return most recent transaction(journal) date or null when never used before. * @@ -136,52 +99,6 @@ interface CategoryRepositoryInterface */ public function lastUseDate(Category $category, Collection $accounts): ?Carbon; - /** - * TODO not multi-currency - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; - - - - /** - * TODO not multi-currency - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * TODO not multi-currency - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * TODO not multi-currency - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; - - - /** * @param string $query * @@ -189,48 +106,11 @@ interface CategoryRepositoryInterface */ public function searchCategory(string $query): Collection; - - /** * @param User $user */ public function setUser(User $user); - /** - * Returns the amount spent in a category, for a set of accounts, in a specific period. - * - * @param Category $category - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * A very cryptic method name that means: - * - * Get me the amount spent in this period, grouped per currency, where no category was set. - * - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array; - - /** - * @param Collection $categories - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return array - */ - public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; - /** * @param array $data * diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php new file mode 100644 index 0000000000..287999e356 --- /dev/null +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -0,0 +1,228 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Category; + + +use Carbon\Carbon; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\User; +use Illuminate\Support\Collection; +use Log; + +/** + * + * Class NoCategoryRepository + */ +class NoCategoryRepository implements NoCategoryRepositoryInterface +{ + /** @var User */ + private $user; + + /** + * Constructor. + */ + public function __construct() + { + if ('testing' === config('app.env')) { + Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); + } + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + + /** + * A very cryptic method name that means: + * + * Get me the amount earned in this period, grouped per currency, where no category was set. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->withoutCategory(); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + $journals = $collector->getExtractedJournals(); + $return = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + if (!isset($return[$currencyId])) { + $return[$currencyId] = [ + 'earned' => '0', + 'currency_id' => $currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + } + $return[$currencyId]['earned'] = bcadd($return[$currencyId]['earned'], $journal['amount']); + } + + return $return; + } + + + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array + { + $carbonFormat = Navigation::preferredCarbonFormat($start, $end); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation(); + $collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]); + $collector->withoutCategory(); + $journals = $collector->getExtractedJournals(); + $result = [ + 'entries' => [], + 'name' => (string)trans('firefly.no_category'), + 'sum' => '0', + ]; + + /** @var array $journal */ + foreach ($journals as $journal) { + $date = $journal['date']->format($carbonFormat); + + if (!isset($result['entries'][$date])) { + $result['entries'][$date] = '0'; + } + $result['entries'][$date] = bcadd($result['entries'][$date], $journal['amount']); + } + + return $result; + } + + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array + { + Log::debug('Now in periodIncomeNoCategory()'); + $carbonFormat = Navigation::preferredCarbonFormat($start, $end); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setAccounts($accounts)->setRange($start, $end)->withAccountInformation(); + $collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]); + $collector->withoutCategory(); + $journals = $collector->getExtractedJournals(); + $result = [ + 'entries' => [], + 'name' => (string)trans('firefly.no_category'), + 'sum' => '0', + ]; + Log::debug('Looping transactions..'); + + foreach ($journals as $journal) { + $date = $journal['date']->format($carbonFormat); + + if (!isset($result['entries'][$date])) { + $result['entries'][$date] = '0'; + } + $result['entries'][$date] = bcadd($result['entries'][$date], bcmul($journal['amount'],'-1')); + } + Log::debug('Done looping transactions..'); + Log::debug('Finished periodIncomeNoCategory()'); + + return $result; + } + + + /** + * A very cryptic method name that means: + * + * Get me the amount spent in this period, grouped per currency, where no category was set. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->withoutCategory(); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + + $set = $collector->getExtractedJournals(); + $return = []; + /** @var array $journal */ + foreach ($set as $journal) { + $currencyId = (int)$journal['currency_id']; + if (!isset($return[$currencyId])) { + $return[$currencyId] = [ + 'spent' => '0', + 'currency_id' => $currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + } + $return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $journal['amount']); + } + + return $return; + } + +} \ No newline at end of file diff --git a/app/Repositories/Category/NoCategoryRepositoryInterface.php b/app/Repositories/Category/NoCategoryRepositoryInterface.php new file mode 100644 index 0000000000..630523a3fb --- /dev/null +++ b/app/Repositories/Category/NoCategoryRepositoryInterface.php @@ -0,0 +1,99 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Category; + + +use Carbon\Carbon; +use FireflyIII\User; +use Illuminate\Support\Collection; + +/** + * Interface NoCategoryRepositoryInterface + * + * @package FireflyIII\Repositories\Category + */ +interface NoCategoryRepositoryInterface +{ + /** + * @param User $user + */ + public function setUser(User $user): void; + + /** + * A very cryptic method name that means: + * + * Get me the amount earned in this period, grouped per currency, where no category was set. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function earnedInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array; + + + /** + * TODO not multi-currency + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function periodExpensesNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; + + + /** + * TODO not multi-currency + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function periodIncomeNoCategory(Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * A very cryptic method name that means: + * + * Get me the amount spent in this period, grouped per currency, where no category was set. + * + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + * + */ + public function spentInPeriodPcWoCategory(Collection $accounts, Carbon $start, Carbon $end): array; + + +} \ No newline at end of file diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php new file mode 100644 index 0000000000..0cf5527738 --- /dev/null +++ b/app/Repositories/Category/OperationsRepository.php @@ -0,0 +1,375 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Category; + + +use Carbon\Carbon; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\Category; +use FireflyIII\Models\TransactionType; +use FireflyIII\User; +use Illuminate\Support\Collection; +use Log; + +/** + * + * Class OperationsRepository + */ +class OperationsRepository implements OperationsRepositoryInterface +{ + /** @var User */ + private $user; + + /** + * Constructor. + */ + public function __construct() + { + if ('testing' === config('app.env')) { + Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); + } + } + + /** + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function earnedInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT])->setCategory($category); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + // collect and group results: + $array = $collector->getExtractedJournals(); + $return = []; + + foreach ($array as $journal) { + $currencyCode = $journal['currency_code']; + if (!isset($return[$currencyCode])) { + $return[$currencyCode] = [ + 'currency_id' => $journal['currency_id'], + 'currency_code' => $journal['currency_code'], + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + 'earned' => '0', + ]; + } + + // also extract foreign currency information: + if (null !== $journal['foreign_currency_id']) { + $currencyCode = $journal['foreign_currency_code']; + if (!isset($return[$currencyCode])) { + $return[$currencyCode] = [ + 'currency_id' => $journal['foreign_currency_id'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + 'earned' => '0', + ]; + } + $return[$currencyCode]['earned'] = bcadd($return[$currencyCode]['earned'], app('steam')->positive($journal['foreign_amount'])); + } + $return[$currencyCode]['earned'] = bcadd($return[$currencyCode]['earned'], app('steam')->positive($journal['amount'])); + } + + return $return; + } + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::DEPOSIT]); + + if ($categories->count() > 0) { + $collector->setCategories($categories); + } + if (0 === $categories->count()) { + $collector->setCategories($this->getCategories()); + } + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + $journals = $collector->getExtractedJournals(); + $return = []; + foreach ($journals as $journal) { + $categoryId = (int)$journal['category_id']; + $currencyId = (int)$journal['currency_id']; + $name = $journal['category_name']; + // make array for category: + if (!isset($return[$categoryId])) { + $return[$categoryId] = [ + 'name' => $name, + 'earned' => [], + ]; + } + if (!isset($return[$categoryId]['earned'][$currencyId])) { + $return[$categoryId]['earned'][$currencyId] = [ + 'earned' => '0', + 'currency_id' => $currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + } + $return[$categoryId]['earned'][$currencyId]['earned'] + = bcadd($return[$categoryId]['earned'][$currencyId]['earned'], $journal['amount']); + } + + return $return; + } + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array + { + $carbonFormat = Navigation::preferredCarbonFormat($start, $end); + $data = []; + // prep data array: + /** @var Category $category */ + foreach ($categories as $category) { + $data[$category->id] = [ + 'name' => $category->name, + 'sum' => '0', + 'entries' => [], + ]; + } + + // get all transactions: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setAccounts($accounts)->setRange($start, $end); + $collector->setCategories($categories)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->withAccountInformation()->withCategoryInformation(); + $journals = $collector->getExtractedJournals(); + + // loop transactions: + + foreach ($journals as $journal) { + $categoryId = (int)$journal['category_id']; + $date = $journal['date']->format($carbonFormat); + $data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', $journal['amount']); + } + + return $data; + } + + /** + * TODO not multi currency aware. + * + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array + { + $carbonFormat = Navigation::preferredCarbonFormat($start, $end); + $data = []; + // prep data array: + /** @var Category $category */ + foreach ($categories as $category) { + $data[$category->id] = [ + 'name' => $category->name, + 'sum' => '0', + 'entries' => [], + ]; + } + + // get all transactions: + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + + $collector->setAccounts($accounts)->setRange($start, $end); + $collector->setCategories($categories)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->withAccountInformation(); + $journals = $collector->getExtractedJournals(); + + // loop transactions: + /** @var array $journal */ + foreach ($journals as $journal) { + $categoryId = (int)$journal['category_id']; + $date = $journal['date']->format($carbonFormat); + $data[$categoryId]['entries'][$date] = bcadd($data[$categoryId]['entries'][$date] ?? '0', bcmul($journal['amount'], '-1')); + } + + return $data; + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + + /** + * Returns the amount spent in a category, for a set of accounts, in a specific period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setCategory($category); + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + // collect and group results: + $array = $collector->getExtractedJournals(); + $return = []; + + foreach ($array as $journal) { + $currencyCode = $journal['currency_code']; + if (!isset($return[$currencyCode])) { + $return[$currencyCode] = [ + 'currency_id' => $journal['currency_id'], + 'currency_code' => $journal['currency_code'], + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + 'spent' => '0', + ]; + } + + // also extract foreign currency information: + if (null !== $journal['foreign_currency_id']) { + $currencyCode = $journal['foreign_currency_code']; + if (!isset($return[$currencyCode])) { + $return[$currencyCode] = [ + 'currency_id' => $journal['foreign_currency_id'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + 'spent' => '0', + ]; + } + $return[$currencyCode]['spent'] = bcadd($return[$currencyCode]['spent'], $journal['foreign_amount']); + } + $return[$currencyCode]['spent'] = bcadd($return[$currencyCode]['spent'], $journal['amount']); + } + + return $return; + } + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + */ + public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user); + $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]); + + if ($categories->count() > 0) { + $collector->setCategories($categories); + } + if (0 === $categories->count()) { + $collector->setCategories($this->getCategories()); + } + + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + + $set = $collector->getExtractedJournals(); + $return = []; + /** @var array $journal */ + foreach ($set as $journal) { + $categoryId = (int)$journal['category_id']; + $currencyId = (int)$journal['currency_id']; + $name = $journal['category_name']; + + // make array for category: + if (!isset($return[$categoryId])) { + $return[$categoryId] = [ + 'name' => $name, + 'spent' => [], + ]; + } + if (!isset($return[$categoryId]['spent'][$currencyId])) { + $return[$categoryId]['spent'][$currencyId] = [ + 'spent' => '0', + 'currency_id' => $currencyId, + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + } + $return[$categoryId]['spent'][$currencyId]['spent'] + = bcadd($return[$categoryId]['spent'][$currencyId]['spent'], $journal['amount']); + } + + return $return; + } +} \ No newline at end of file diff --git a/app/Repositories/Category/OperationsRepositoryInterface.php b/app/Repositories/Category/OperationsRepositoryInterface.php new file mode 100644 index 0000000000..3cd72991b1 --- /dev/null +++ b/app/Repositories/Category/OperationsRepositoryInterface.php @@ -0,0 +1,114 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Repositories\Category; + +use Carbon\Carbon; +use FireflyIII\Models\Category; +use FireflyIII\User; +use Illuminate\Support\Collection; + +/** + * Interface OperationsRepositoryInterface + * + * @package FireflyIII\Repositories\Category + */ +interface OperationsRepositoryInterface +{ + /** + * Returns the amount earned in a category, for a set of accounts, in a specific period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + * + */ + public function earnedInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function earnedInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * @param User $user + */ + public function setUser(User $user): void; + + /** + * Returns the amount spent in a category, for a set of accounts, in a specific period. + * + * @param Category $category + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + * + */ + public function spentInPeriod(Category $category, Collection $accounts, Carbon $start, Carbon $end): array; + + /** + * @param Collection $categories + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return array + * @deprecated + */ + public function spentInPeriodPerCurrency(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; +} \ No newline at end of file diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index dc3518616b..e067888192 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -27,6 +27,7 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\Category; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; /** * Class WholePeriodChartGenerator @@ -45,6 +46,9 @@ class WholePeriodChartGenerator /** @var CategoryRepositoryInterface $repository */ $repository = app(CategoryRepositoryInterface::class); + /** @var OperationsRepositoryInterface $opsRepository */ + $opsRepository = app(OperationsRepositoryInterface::class); + /** @var AccountRepositoryInterface $accountRepository */ $accountRepository = app(AccountRepositoryInterface::class); @@ -61,8 +65,8 @@ class WholePeriodChartGenerator while ($current <= $end) { $key = $current->format('Y-m-d'); $currentEnd = app('navigation')->endOfPeriod($current, $step); - $spent[$key] = $repository->spentInPeriod($category, $accounts, $current, $currentEnd); - $earned[$key] = $repository->earnedInPeriod($category, $accounts, $current, $currentEnd); + $spent[$key] = $opsRepository->spentInPeriod($category, $accounts, $current, $currentEnd); + $earned[$key] = $opsRepository->earnedInPeriod($category, $accounts, $current, $currentEnd); $current = app('navigation')->addPeriod($current, $step, 0); } $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index 794fc4fc25..da13d1b164 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -26,6 +26,7 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use Illuminate\Support\Collection; use Log; @@ -34,6 +35,8 @@ use Log; */ class CategoryTransformer extends AbstractTransformer { + /** @var OperationsRepositoryInterface */ + private $opsRepository; /** @var CategoryRepositoryInterface */ private $repository; @@ -44,7 +47,8 @@ class CategoryTransformer extends AbstractTransformer */ public function __construct() { - $this->repository = app(CategoryRepositoryInterface::class); + $this->repository = app(CategoryRepositoryInterface::class); + $this->opsRepository = app(OperationsRepositoryInterface::class); if ('testing' === config('app.env')) { Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); } @@ -60,13 +64,15 @@ class CategoryTransformer extends AbstractTransformer public function transform(Category $category): array { $this->repository->setUser($category->user); + $this->opsRepository->setUser($category->user); + $spent = []; $earned = []; $start = $this->parameters->get('start'); $end = $this->parameters->get('end'); if (null !== $start && null !== $end) { - $earned = array_values($this->repository->earnedInPeriod($category, new Collection, $start, $end)); - $spent = array_values($this->repository->spentInPeriod($category, new Collection, $start, $end)); + $earned = array_values($this->opsRepository->earnedInPeriod($category, new Collection, $start, $end)); + $spent = array_values($this->opsRepository->spentInPeriod($category, new Collection, $start, $end)); } $data = [ 'id' => (int)$category->id,