From 91394553c34600131e05f1cabea4d7c59cb45cde Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 7 Mar 2021 15:26:42 +0100 Subject: [PATCH] Add some routes for transfers. --- .../Insight/Transfer/AccountController.php | 85 +++++++++ .../Insight/Transfer/CategoryController.php | 125 +++++++++++++ .../Insight/Transfer/PeriodController.php | 82 +++++++++ .../Insight/Transfer/TagController.php | 173 ++++++++++++++++++ .../V1/Requests/Insight/GenericRequest.php | 2 +- app/Handlers/Events/WebhookEventHandler.php | 4 +- .../Account/OperationsRepository.php | 66 +++++++ .../Account/OperationsRepositoryInterface.php | 12 ++ .../Category/NoCategoryRepository.php | 35 +++- .../NoCategoryRepositoryInterface.php | 11 ++ .../Category/OperationsRepository.php | 43 +++++ .../OperationsRepositoryInterface.php | 12 ++ .../Webhook/WebhookRepository.php | 22 ++- .../Webhook/StandardWebhookSender.php | 2 +- routes/api.php | 165 +++-------------- 15 files changed, 682 insertions(+), 157 deletions(-) create mode 100644 app/Api/V1/Controllers/Insight/Transfer/AccountController.php create mode 100644 app/Api/V1/Controllers/Insight/Transfer/CategoryController.php create mode 100644 app/Api/V1/Controllers/Insight/Transfer/PeriodController.php create mode 100644 app/Api/V1/Controllers/Insight/Transfer/TagController.php diff --git a/app/Api/V1/Controllers/Insight/Transfer/AccountController.php b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php new file mode 100644 index 0000000000..ad113a6325 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/AccountController.php @@ -0,0 +1,85 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Repositories\Account\OperationsRepositoryInterface; +use FireflyIII\Support\Http\Api\ApiSupport; +use Illuminate\Http\JsonResponse; + +/** + * Class AccountController + */ +class AccountController extends Controller +{ + use ApiSupport; + + private OperationsRepositoryInterface $opsRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->opsRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * TODO same code as Expense/AccountController. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function asset(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $income = $this->opsRepository->sumTransfers($start, $end, $assetAccounts); + $result = []; + /** @var array $entry */ + foreach ($income as $entry) { + $result[] = [ + 'difference' => $entry['sum'], + 'difference_float' => (float)$entry['sum'], + 'currency_id' => (string)$entry['currency_id'], + 'currency_code' => $entry['currency_code'], + ]; + } + + return response()->json($result); + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php new file mode 100644 index 0000000000..1f13918a0f --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/CategoryController.php @@ -0,0 +1,125 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Models\Category; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Collection; + +/** + * Class CategoryController + */ +class CategoryController extends Controller +{ + private OperationsRepositoryInterface $opsRepository; + private CategoryRepositoryInterface $repository; + private NoCategoryRepositoryInterface $noRepository; + + /** + * AccountController constructor. + * + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $this->opsRepository = app(OperationsRepositoryInterface::class); + $this->repository = app(CategoryRepositoryInterface::class); + $this->noRepository = app(NoCategoryRepositoryInterface::class); + $user = auth()->user(); + $this->opsRepository->setUser($user); + $this->repository->setUser($user); + $this->noRepository->setUser($user); + + return $next($request); + } + ); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function category(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $categories = $request->getCategories(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + if (0 === $categories->count()) { + $categories = $this->repository->getCategories(); + } + /** @var Category $category */ + foreach ($categories as $category) { + $expenses = $this->opsRepository->sumTransfers($start, $end, $assetAccounts, new Collection([$category])); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'id' => (string)$category->id, + 'name' => $category->name, + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + } + + return response()->json($result); + } + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noCategory(GenericRequest $request): JsonResponse + { + $start = $request->getStart(); + $end = $request->getEnd(); + $assetAccounts = $request->getAssetAccounts(); + $result = []; + $expenses = $this->noRepository->sumTransfers($start, $end, $assetAccounts); + /** @var array $expense */ + foreach ($expenses as $expense) { + $result[] = [ + 'difference' => $expense['sum'], + 'difference_float' => (float)$expense['sum'], + 'currency_id' => (string)$expense['currency_id'], + 'currency_code' => $expense['currency_code'], + ]; + } + + return response()->json($result); + + } +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php new file mode 100644 index 0000000000..b03788c0d3 --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/PeriodController.php @@ -0,0 +1,82 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use Illuminate\Http\JsonResponse; + +/** + * Class PeriodController + */ +class PeriodController extends Controller +{ + + /** + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function total(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $genericSet = $collector->getExtractedJournals(); + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } + +} \ No newline at end of file diff --git a/app/Api/V1/Controllers/Insight/Transfer/TagController.php b/app/Api/V1/Controllers/Insight/Transfer/TagController.php new file mode 100644 index 0000000000..5001cf21bb --- /dev/null +++ b/app/Api/V1/Controllers/Insight/Transfer/TagController.php @@ -0,0 +1,173 @@ +. + */ + +namespace FireflyIII\Api\V1\Controllers\Insight\Transfer; + + +use FireflyIII\Api\V1\Controllers\Controller; +use FireflyIII\Api\V1\Requests\Insight\GenericRequest; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; +use FireflyIII\Models\TransactionType; +use FireflyIII\Repositories\Tag\TagRepositoryInterface; +use Illuminate\Http\JsonResponse; + +/** + * Class TagController + */ +class TagController extends Controller +{ + private TagRepositoryInterface $repository; + + /** + * TagController constructor. + * TODO lots of copying and pasting here. + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + $user = auth()->user(); + $this->repository = app(TagRepositoryInterface::class); + $this->repository->setUser($user); + + return $next($request); + } + ); + } + + /** + * Transfers per tag, possibly filtered by tag and account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function tag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $tags = $request->getTags(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // get all tags: + if (0 === $tags->count()) { + $tags = $this->repository->get(); + } + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->setTags($tags); + $genericSet = $collector->getExtractedJournals(); + /** @var array $entry */ + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + /** @var array $tag */ + foreach ($journal['tags'] as $tag) { + $tagId = $tag['id']; + $key = sprintf('%d-%d', $tagId, $currencyId); + $foreignKey = sprintf('%d-%d', $tagId, $foreignCurrencyId); + + // on currency ID + if (0 !== $currencyId) { + $response[$key] = $response[$key] ?? [ + 'id' => (string)$tagId, + 'name' => $tag['name'], + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$key]['difference'] = bcadd($response[$key]['difference'], app('steam')->positive($journal['amount'])); + $response[$key]['difference_float'] = (float)$response[$key]['difference']; + } + + // on foreign ID + if (0 !== $foreignCurrencyId) { + $response[$foreignKey] = $journal[$foreignKey] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignKey]['difference'] = bcadd($response[$foreignKey]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignKey]['difference_float'] = (float)$response[$foreignKey]['difference']; + } + } + } + + return response()->json(array_values($response)); + } + + /** + * Expenses for no tag filtered by account. + * + * @param GenericRequest $request + * + * @return JsonResponse + */ + public function noTag(GenericRequest $request): JsonResponse + { + $accounts = $request->getAssetAccounts(); + $start = $request->getStart(); + $end = $request->getEnd(); + $response = []; + + // collect all expenses in this period (regardless of type) by the given bills and accounts. + $collector = app(GroupCollectorInterface::class); + $collector->setTypes([TransactionType::TRANSFER])->setRange($start, $end)->setDestinationAccounts($accounts); + $collector->withoutTags(); + + $genericSet = $collector->getExtractedJournals(); + + foreach ($genericSet as $journal) { + $currencyId = (int)$journal['currency_id']; + $foreignCurrencyId = (int)$journal['foreign_currency_id']; + + if (0 !== $currencyId) { + $response[$currencyId] = $response[$currencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$currencyId, + 'currency_code' => $journal['currency_code'], + ]; + $response[$currencyId]['difference'] = bcadd($response[$currencyId]['difference'], app('steam')->positive($journal['amount'])); + $response[$currencyId]['difference_float'] = (float)$response[$currencyId]['difference']; + } + if (0 !== $foreignCurrencyId) { + $response[$foreignCurrencyId] = $response[$foreignCurrencyId] ?? [ + 'difference' => '0', + 'difference_float' => 0, + 'currency_id' => (string)$foreignCurrencyId, + 'currency_code' => $journal['foreign_currency_code'], + ]; + $response[$foreignCurrencyId]['difference'] = bcadd($response[$foreignCurrencyId]['difference'], app('steam')->positive($journal['foreign_amount'])); + $response[$foreignCurrencyId]['difference_float'] = (float)$response[$foreignCurrencyId]['difference']; + } + } + + return response()->json(array_values($response)); + } +} \ No newline at end of file diff --git a/app/Api/V1/Requests/Insight/GenericRequest.php b/app/Api/V1/Requests/Insight/GenericRequest.php index ce67059a71..0ebac45ef1 100644 --- a/app/Api/V1/Requests/Insight/GenericRequest.php +++ b/app/Api/V1/Requests/Insight/GenericRequest.php @@ -247,7 +247,7 @@ class GenericRequest extends FormRequest if (is_array($array)) { foreach ($array as $billId) { $billId = (int)$billId; - $bill = $repository->findNull($billId); + $bill = $repository->find($billId); if (null !== $billId) { $this->bills->push($bill); } diff --git a/app/Handlers/Events/WebhookEventHandler.php b/app/Handlers/Events/WebhookEventHandler.php index 908d18a625..cb6a4ea66e 100644 --- a/app/Handlers/Events/WebhookEventHandler.php +++ b/app/Handlers/Events/WebhookEventHandler.php @@ -61,13 +61,13 @@ class WebhookEventHandler // kick off the job! $messages = WebhookMessage ::where('webhook_messages.sent', 0) - ->where('webhook_messages.errored', 0) + //->where('webhook_messages.errored', 0) ->get(['webhook_messages.*']) ->filter( function (WebhookMessage $message) { return $message->webhookAttempts()->count() <= 2; } - )->splice(0, 3); + )->splice(0, 5); Log::debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count())); foreach ($messages as $message) { SendWebhookMessage::dispatch($message)->afterResponse(); diff --git a/app/Repositories/Account/OperationsRepository.php b/app/Repositories/Account/OperationsRepository.php index 2525e88b23..4492585e02 100644 --- a/app/Repositories/Account/OperationsRepository.php +++ b/app/Repositories/Account/OperationsRepository.php @@ -311,4 +311,70 @@ class OperationsRepository implements OperationsRepositoryInterface } return $array; } + + /** + * @inheritDoc + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array + { + $start->startOfDay(); + $end->endOfDay(); + + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER]); + + if (null !== $accounts) { + $collector->setAccounts($accounts); + } + if (null !== $currency) { + $collector->setCurrency($currency); + } + $journals = $collector->getExtractedJournals(); + + // same but for foreign currencies: + if (null !== $currency) { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL]) + ->setForeignCurrency($currency); + + if (null !== $accounts) { + $collector->setAccounts($accounts); + } + $result = $collector->getExtractedJournals(); + + // do not use array_merge because you want keys to overwrite (otherwise you get double results): + $journals = $result + $journals; + } + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + + // also do foreign amount: + $foreignId = (int)$journal['foreign_currency_id']; + if (0 !== $foreignId) { + $array[$foreignId] = $array[$foreignId] ?? [ + 'sum' => '0', + 'currency_id' => $foreignId, + 'currency_name' => $journal['foreign_currency_name'], + 'currency_symbol' => $journal['foreign_currency_symbol'], + 'currency_code' => $journal['foreign_currency_code'], + 'currency_decimal_places' => $journal['foreign_currency_decimal_places'], + ]; + $array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->positive($journal['foreign_amount'])); + } + } + return $array; + } } diff --git a/app/Repositories/Account/OperationsRepositoryInterface.php b/app/Repositories/Account/OperationsRepositoryInterface.php index e0cdeed9ec..d8644af5be 100644 --- a/app/Repositories/Account/OperationsRepositoryInterface.php +++ b/app/Repositories/Account/OperationsRepositoryInterface.php @@ -89,4 +89,16 @@ interface OperationsRepositoryInterface * @return array */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $revenue = null, ?TransactionCurrency $currency = null): array; + + /** + * Sum of transfers in period for a set of accounts, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param TransactionCurrency|null $currency + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array; } diff --git a/app/Repositories/Category/NoCategoryRepository.php b/app/Repositories/Category/NoCategoryRepository.php index e86df31d12..6883394a52 100644 --- a/app/Repositories/Category/NoCategoryRepository.php +++ b/app/Repositories/Category/NoCategoryRepository.php @@ -29,7 +29,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionType; use FireflyIII\User; use Illuminate\Support\Collection; -use Log; /** * @@ -37,8 +36,7 @@ use Log; */ class NoCategoryRepository implements NoCategoryRepositoryInterface { - /** @var User */ - private $user; + private User $user; /** * This method returns a list of all the withdrawal transaction journals (as arrays) set in that period @@ -229,4 +227,35 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface return $array; } + + /** + * @inheritDoc + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])->withoutCategory(); + + if (null !== $accounts && $accounts->count() > 0) { + $collector->setAccounts($accounts); + } + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + } + + return $array; + } } diff --git a/app/Repositories/Category/NoCategoryRepositoryInterface.php b/app/Repositories/Category/NoCategoryRepositoryInterface.php index dd82a6aa2c..592526bd09 100644 --- a/app/Repositories/Category/NoCategoryRepositoryInterface.php +++ b/app/Repositories/Category/NoCategoryRepositoryInterface.php @@ -87,5 +87,16 @@ interface NoCategoryRepositoryInterface */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + /** + * Sum of transfers in period without a category, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null): array; + } diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php index f20573c630..4e4136cf9d 100644 --- a/app/Repositories/Category/OperationsRepository.php +++ b/app/Repositories/Category/OperationsRepository.php @@ -294,6 +294,49 @@ class OperationsRepository implements OperationsRepositoryInterface return $array; } + /** + * Sum of income journals in period for a set of categories, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $categories + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array + { + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setUser($this->user)->setRange($start, $end) + ->setTypes([TransactionType::TRANSFER]); + + if (null !== $accounts && $accounts->count() > 0) { + $collector->setAccounts($accounts); + } + if (null === $categories || (null !== $categories && 0 === $categories->count())) { + $categories = $this->getCategories(); + } + $collector->setCategories($categories); + $journals = $collector->getExtractedJournals(); + $array = []; + + foreach ($journals as $journal) { + $currencyId = (int)$journal['currency_id']; + $array[$currencyId] = $array[$currencyId] ?? [ + 'sum' => '0', + 'currency_id' => $currencyId, + 'currency_name' => $journal['currency_name'], + 'currency_symbol' => $journal['currency_symbol'], + 'currency_code' => $journal['currency_code'], + 'currency_decimal_places' => $journal['currency_decimal_places'], + ]; + $array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->positive($journal['amount'])); + } + + return $array; + } + /** * Returns a list of all the categories belonging to a user. * diff --git a/app/Repositories/Category/OperationsRepositoryInterface.php b/app/Repositories/Category/OperationsRepositoryInterface.php index f2fa4b4e5d..74fc870394 100644 --- a/app/Repositories/Category/OperationsRepositoryInterface.php +++ b/app/Repositories/Category/OperationsRepositoryInterface.php @@ -89,4 +89,16 @@ interface OperationsRepositoryInterface * @return array */ public function sumIncome(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; + + /** + * Sum of transfers in period for a set of categories, grouped per currency. Amounts are always positive. + * + * @param Carbon $start + * @param Carbon $end + * @param Collection|null $accounts + * @param Collection|null $categories + * + * @return array + */ + public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array; } diff --git a/app/Repositories/Webhook/WebhookRepository.php b/app/Repositories/Webhook/WebhookRepository.php index 274b055e14..14de2dcb8e 100644 --- a/app/Repositories/Webhook/WebhookRepository.php +++ b/app/Repositories/Webhook/WebhookRepository.php @@ -104,7 +104,7 @@ class WebhookRepository implements WebhookRepositoryInterface $webhook->delivery = $data['delivery'] ?? $webhook->delivery; $webhook->url = $data['url'] ?? $webhook->url; - if(true === $data['secret']) { + if (true === $data['secret']) { $secret = $random = Str::random(24); $webhook->secret = $secret; } @@ -144,21 +144,23 @@ class WebhookRepository implements WebhookRepositoryInterface public function getReadyMessages(Webhook $webhook): Collection { return $webhook->webhookMessages() - ->where('webhook_messages.sent', 0) - ->where('webhook_messages.errored', 0) - ->get(['webhook_messages.*']) - ->filter( - function (WebhookMessage $message) { - return $message->webhookAttempts()->count() <= 2; - } - )->splice(0, 3); + ->where('webhook_messages.sent', 0) + ->where('webhook_messages.errored', 0) + ->get(['webhook_messages.*']) + ->filter( + function (WebhookMessage $message) { + return $message->webhookAttempts()->count() <= 2; + } + )->splice(0, 3); } + /** * @inheritDoc */ public function getMessages(Webhook $webhook): Collection { return $webhook->webhookMessages() + ->orderBy('created_at', 'DESC') ->get(['webhook_messages.*']); } @@ -167,6 +169,6 @@ class WebhookRepository implements WebhookRepositoryInterface */ public function getAttempts(WebhookMessage $webhookMessage): Collection { - return $webhookMessage->webhookAttempts; + return $webhookMessage->webhookAttempts()->orderBy('created_at', 'DESC')->get(['webhook_attempts.*']); } } diff --git a/app/Services/Webhook/StandardWebhookSender.php b/app/Services/Webhook/StandardWebhookSender.php index 9f3e720050..6fdb275569 100644 --- a/app/Services/Webhook/StandardWebhookSender.php +++ b/app/Services/Webhook/StandardWebhookSender.php @@ -118,7 +118,7 @@ class StandardWebhookSender implements WebhookSenderInterface ]; $client = new Client; try { - $res = $client->request('POST', $this->message->webhook->url, $options); + $res = $client->request('POST', $this->message->webhook->url . 'x', $options); $this->message->sent = true; } catch (ClientException | Exception $e) { Log::error($e->getMessage()); diff --git a/routes/api.php b/routes/api.php index 4c34044abe..73b084a72a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -114,15 +114,10 @@ Route::group( Route::get('tag', ['uses' => 'TagController@tag', 'as' => 'tag']); Route::get('no-tag', ['uses' => 'TagController@noTag', 'as' => 'no-tag']); - - // TODO per budget limit? - // TODO per object group? - // TODO per recurrence? - // TODO per object group - // TODO transfers voor piggies - // TODO transfers per piggy? - // TODO currency? - // TODO net worth? + // TODO Per object group, maybe in the future. + // TODO Per recurrence, all transactions created under it. + // TODO Per currency, or as a filter? + // TODO Show user net worth? } ); // insight in income @@ -140,21 +135,29 @@ Route::group( Route::get('tag', ['uses' => 'TagController@tag', 'as' => 'tag']); Route::get('no-tag', ['uses' => 'TagController@noTag', 'as' => 'no-tag']); - // TODO per budget limit? - // TODO per object group? - // TODO per recurrence? - // TODO per object group - // TODO transfers voor piggies - // TODO transfers per piggy? - // TODO currency? - // TODO net worth? + // TODO Per object group, maybe in the future. + // TODO Per recurrence, all transactions created under it. + // TODO Per currency, or as a filter? + // TODO Show user net worth? } ); - // Insight in transfers -// TODO +Route::group( + ['namespace' => 'FireflyIII\Api\V1\Controllers\Insight\Transfer', 'prefix' => 'insight/transfer', + 'as' => 'api.v1.insight.income.',], + static function () { + // Insight in expenses per account: + Route::get('asset', ['uses' => 'AccountController@asset', 'as' => 'asset']); + Route::get('category', ['uses' => 'CategoryController@category', 'as' => 'category']); + Route::get('no-category', ['uses' => 'CategoryController@noCategory', 'as' => 'no-category']); + Route::get('tag', ['uses' => 'TagController@tag', 'as' => 'tag']); + Route::get('no-tag', ['uses' => 'TagController@noTag', 'as' => 'no-tag']); + Route::get('total', ['uses' => 'PeriodController@total', 'as' => 'total']); + // TODO Transfers for piggies + } +); /** @@ -508,7 +511,6 @@ Route::group( ); - // Users API routes: Route::group( ['middleware' => ['auth:api', 'bindings', IsAdmin::class], 'namespace' => 'FireflyIII\Api\V1\Controllers\System', 'prefix' => 'users', @@ -559,126 +561,9 @@ Route::group( // webhook message attempts Route::get('{webhook}/messages/{webhookMessage}/attempts', ['uses' => 'AttemptController@index', 'as' => 'attempts.index']); Route::get('{webhook}/messages/{webhookMessage}/attempts/{webhookAttempt}', ['uses' => 'AttemptController@show', 'as' => 'attempts.show']); - Route::delete('{webhook}/messages/{webhookMessage}/attempts/{webhookAttempt}', ['uses' => 'DestroyController@destroyAttempt', 'as' => 'attempts.destroy']); + Route::delete( + '{webhook}/messages/{webhookMessage}/attempts/{webhookAttempt}', ['uses' => 'DestroyController@destroyAttempt', 'as' => 'attempts.destroy'] + ); } ); - - - - - - - - - - - - - - - - - - - - - - - - - - -/** - * DATA CONTROLLERS - * - */ - -//// EXPORT -//Route::group( -// ['namespace' => 'FireflyIII\Api\V1\Controllers\Data\Export', 'prefix' => 'data/export', -// 'as' => 'api.v1.data.export.',], -// static function () { -// Route::get('transactions', ['uses' => 'TransactionController@export', 'as' => 'transactions']); -// } -//); - -/** - * INSIGHT CONTROLLERS - */ - - -/** - * System and configuration controllers - */ - -// TODO get rid of underscores. - - - - - - - - - -//// TODO VERIFY API DOCS - - - - - - - - - -// -// -//// TODO VERIFY API DOCS -//Route::group( -// ['namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'cer', -// 'as' => 'api.v1.cer.',], -// static function () { -// -// // Currency Exchange Rate API routes: -// Route::get('', ['uses' => 'CurrencyExchangeRateController@index', 'as' => 'index']); -// } -//); - - - - - -//); - - - - - - - -// -//// TODO VERIFY API DOCS -//Route::group( -// ['namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'tag-cloud', -// 'as' => 'api.v1.tag-cloud.',], -// static function () { -// // Tag cloud API routes (to prevent collisions) -// Route::get('', ['uses' => 'TagController@cloud', 'as' => 'cloud']); -// } -//); -// - -// -//// special group for transaction journals -//// TODO VERIFY API DOCS -//Route::group( -// ['namespace' => 'FireflyIII\Api\V1\Controllers\Models\Transaction', 'prefix' => 'transaction-journals', -// 'as' => 'api.v1.journals.',], -// static function () { -// -// // Transaction API routes: -// Route::get('{tj}', ['uses' => 'ShowController@showByJournal', 'as' => 'showByJournal']); -// } -//); -//