mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'develop' into adminlte4
This commit is contained in:
commit
fc0fee161e
@ -65,12 +65,16 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Documentation for this endpoint:
|
* Documentation for this endpoint:
|
||||||
* TODO endpoint is not documented.
|
* TODO list of checks
|
||||||
|
* 1. use dates from ParameterBag
|
||||||
|
* 2. Request validates dates
|
||||||
|
* 3. Request includes user_group_id as administration_id
|
||||||
|
* 4. Endpoint is documented.
|
||||||
|
* 5. Collector uses administration_id
|
||||||
*
|
*
|
||||||
* @param AutocompleteRequest $request
|
* @param AutocompleteRequest $request
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
* @throws JsonException
|
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
@ -79,7 +83,7 @@ class AccountController extends Controller
|
|||||||
$data = $request->getData();
|
$data = $request->getData();
|
||||||
$types = $data['types'];
|
$types = $data['types'];
|
||||||
$query = $data['query'];
|
$query = $data['query'];
|
||||||
$date = $data['date'] ?? today(config('app.timezone'));
|
$date = $this->parameters->get('date') ?? today(config('app.timezone'));
|
||||||
$this->adminRepository->setAdministrationId($data['administration_id']);
|
$this->adminRepository->setAdministrationId($data['administration_id']);
|
||||||
|
|
||||||
$return = [];
|
$return = [];
|
||||||
|
@ -71,6 +71,8 @@ class AccountController extends Controller
|
|||||||
* If a transaction has foreign currency = native currency, the foreign amount will be used, no conversion
|
* If a transaction has foreign currency = native currency, the foreign amount will be used, no conversion
|
||||||
* will take place.
|
* will take place.
|
||||||
*
|
*
|
||||||
|
* TODO validate and set administration_id from request
|
||||||
|
*
|
||||||
* @param DateRequest $request
|
* @param DateRequest $request
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
@ -80,15 +82,11 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function dashboard(DateRequest $request): JsonResponse
|
public function dashboard(DateRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
// parameters for chart:
|
|
||||||
$dates = $request->getAll();
|
|
||||||
/** @var Carbon $start */
|
/** @var Carbon $start */
|
||||||
$start = $dates['start'];
|
$start = $this->parameters->get('start');
|
||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$end = $dates['end'];
|
$end = $this->parameters->get('end');
|
||||||
$end->endOfDay();
|
$end->endOfDay();
|
||||||
/** @var User $user */
|
|
||||||
$user = auth()->user();
|
|
||||||
|
|
||||||
// user's preferences
|
// user's preferences
|
||||||
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
|
$defaultSet = $this->repository->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT])->pluck('id')->toArray();
|
||||||
|
@ -69,6 +69,9 @@ class BalanceController extends Controller
|
|||||||
* If the transaction being processed is already in native currency OR if the
|
* If the transaction being processed is already in native currency OR if the
|
||||||
* foreign amount is in the native currency, the amount will not be converted.
|
* foreign amount is in the native currency, the amount will not be converted.
|
||||||
*
|
*
|
||||||
|
* TODO validate and set administration_id
|
||||||
|
* TODO collector set group, not user
|
||||||
|
*
|
||||||
* @param BalanceChartRequest $request
|
* @param BalanceChartRequest $request
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
@ -78,9 +81,9 @@ class BalanceController extends Controller
|
|||||||
{
|
{
|
||||||
$params = $request->getAll();
|
$params = $request->getAll();
|
||||||
/** @var Carbon $start */
|
/** @var Carbon $start */
|
||||||
$start = $params['start'];
|
$start = $this->parameters->get('start');
|
||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$end = $params['end'];
|
$end = $this->parameters->get('end');
|
||||||
$end->endOfDay();
|
$end->endOfDay();
|
||||||
/** @var Collection $accounts */
|
/** @var Collection $accounts */
|
||||||
$accounts = $params['accounts'];
|
$accounts = $params['accounts'];
|
||||||
|
@ -69,6 +69,8 @@ class BudgetController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param DateRequest $request
|
* @param DateRequest $request
|
||||||
*
|
*
|
||||||
|
* TODO see autocomplete/accountcontroller
|
||||||
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
|
@ -61,6 +61,7 @@ class CategoryController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO may be worth to move to a handler but the data is simple enough.
|
* TODO may be worth to move to a handler but the data is simple enough.
|
||||||
|
* TODO see autoComplete/account controller
|
||||||
*
|
*
|
||||||
* @param DateRequest $request
|
* @param DateRequest $request
|
||||||
*
|
*
|
||||||
@ -69,11 +70,10 @@ class CategoryController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function dashboard(DateRequest $request): JsonResponse
|
public function dashboard(DateRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$params = $request->getAll();
|
|
||||||
/** @var Carbon $start */
|
/** @var Carbon $start */
|
||||||
$start = $params['start'];
|
$start = $this->parameters->get('start');
|
||||||
/** @var Carbon $end */
|
/** @var Carbon $end */
|
||||||
$end = $params['end'];
|
$end = $this->parameters->get('end');
|
||||||
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT]);
|
$accounts = $this->accountRepos->getAccountsByType([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT]);
|
||||||
$default = app('amount')->getDefaultCurrency();
|
$default = app('amount')->getDefaultCurrency();
|
||||||
$converter = new ExchangeRateConverter();
|
$converter = new ExchangeRateConverter();
|
||||||
|
@ -93,21 +93,25 @@ class Controller extends BaseController
|
|||||||
// some date fields:
|
// some date fields:
|
||||||
foreach ($dates as $field) {
|
foreach ($dates as $field) {
|
||||||
$date = null;
|
$date = null;
|
||||||
|
$obj = null;
|
||||||
try {
|
try {
|
||||||
$date = request()->query->get($field);
|
$date = request()->query->get($field);
|
||||||
} catch (BadRequestException $e) {
|
} catch (BadRequestException $e) {
|
||||||
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
|
Log::error(sprintf('Request field "%s" contains a non-scalar value. Value set to NULL.', $field));
|
||||||
Log::error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
$value = null;
|
|
||||||
}
|
}
|
||||||
$obj = null;
|
|
||||||
if (null !== $date) {
|
if (null !== $date) {
|
||||||
try {
|
try {
|
||||||
$obj = Carbon::parse($date);
|
$obj = Carbon::parse($date, config('app.timezone'));
|
||||||
} catch (InvalidDateException | InvalidFormatException $e) {
|
} catch (InvalidDateException | InvalidFormatException $e) {
|
||||||
// don't care
|
// don't care
|
||||||
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr($date, 0, 20), $e->getMessage()));
|
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr($date, 0, 20), $e->getMessage()));
|
||||||
}
|
}
|
||||||
|
// out of range? set to null.
|
||||||
|
if (null !== $obj && ($obj->year <= 1900 || $obj->year > 2099)) {
|
||||||
|
app('log')->warning(sprintf('Refuse to use date "%s" in API v2 controller parameter check: %s', $field, $obj->toAtomString()));
|
||||||
|
$obj = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$bag->set($field, $obj);
|
$bag->set($field, $obj);
|
||||||
}
|
}
|
||||||
|
72
app/Api/V2/Controllers/Model/Bill/ShowController.php
Normal file
72
app/Api/V2/Controllers/Model/Bill/ShowController.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* ShowController.php
|
||||||
|
* Copyright (c) 2023 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V2\Controllers\Model\Bill;
|
||||||
|
|
||||||
|
use FireflyIII\Api\V2\Controllers\Controller;
|
||||||
|
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
|
||||||
|
use FireflyIII\Transformers\V2\BillTransformer;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ShowController
|
||||||
|
*/
|
||||||
|
class ShowController extends Controller
|
||||||
|
{
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
$this->repository->setAdministrationId(auth()->user()->user_group_id);
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Request $request
|
||||||
|
*
|
||||||
|
* TODO see autocomplete/accountcontroller for list.
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function index(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$this->repository->correctOrder();
|
||||||
|
$bills = $this->repository->getBills();
|
||||||
|
$pageSize = (int)app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||||
|
$count = $bills->count();
|
||||||
|
$bills = $bills->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
|
||||||
|
$paginator = new LengthAwarePaginator($bills, $count, $pageSize, $this->parameters->get('page'));
|
||||||
|
$transformer = new BillTransformer();
|
||||||
|
$transformer->setParameters($this->parameters); // give params to transformer
|
||||||
|
|
||||||
|
return response()
|
||||||
|
->json($this->jsonApiList('subscriptions', $paginator, $transformer))
|
||||||
|
->header('Content-Type', self::CONTENT_TYPE);
|
||||||
|
}
|
||||||
|
}
|
@ -26,7 +26,7 @@ namespace FireflyIII\Api\V2\Controllers\Model\Bill;
|
|||||||
|
|
||||||
use FireflyIII\Api\V2\Controllers\Controller;
|
use FireflyIII\Api\V2\Controllers\Controller;
|
||||||
use FireflyIII\Api\V2\Request\Generic\DateRequest;
|
use FireflyIII\Api\V2\Request\Generic\DateRequest;
|
||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
|
use FireflyIII\Support\Http\Api\ConvertsExchangeRates;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
@ -35,8 +35,6 @@ use Illuminate\Http\JsonResponse;
|
|||||||
*/
|
*/
|
||||||
class SumController extends Controller
|
class SumController extends Controller
|
||||||
{
|
{
|
||||||
use ConvertsExchangeRates;
|
|
||||||
|
|
||||||
private BillRepositoryInterface $repository;
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,35 +56,37 @@ class SumController extends Controller
|
|||||||
* This endpoint is documented at:
|
* This endpoint is documented at:
|
||||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsPaidTrSum
|
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsPaidTrSum
|
||||||
*
|
*
|
||||||
|
* TODO see autocomplete/accountcontroller for list.
|
||||||
|
*
|
||||||
* @param DateRequest $request
|
* @param DateRequest $request
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function paid(DateRequest $request): JsonResponse
|
public function paid(DateRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$dates = $request->getAll();
|
$this->repository->setAdministrationId(auth()->user()->user_group_id);
|
||||||
$result = $this->repository->sumPaidInRange($dates['start'], $dates['end']);
|
$result = $this->repository->sumPaidInRange($this->parameters->get('start'), $this->parameters->get('end'));
|
||||||
$converted = $this->cerSum($result);
|
|
||||||
|
|
||||||
// convert to JSON response:
|
// convert to JSON response:
|
||||||
return response()->api($converted);
|
return response()->api(array_values($result));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This endpoint is documented at:
|
* This endpoint is documented at:
|
||||||
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsUnpaidTrSum
|
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v2)#/transactions-sum/getBillsUnpaidTrSum
|
||||||
*
|
*
|
||||||
|
* TODO see autocomplete/accountcontroller for list.
|
||||||
|
*
|
||||||
* @param DateRequest $request
|
* @param DateRequest $request
|
||||||
*
|
*
|
||||||
* @return JsonResponse
|
* @return JsonResponse
|
||||||
*/
|
*/
|
||||||
public function unpaid(DateRequest $request): JsonResponse
|
public function unpaid(DateRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$dates = $request->getAll();
|
$this->repository->setAdministrationId(auth()->user()->user_group_id);
|
||||||
$result = $this->repository->sumUnpaidInRange($dates['start'], $dates['end']);
|
$result = $this->repository->sumUnpaidInRange($this->parameters->get('start'), $this->parameters->get('end'));
|
||||||
$converted = $this->cerSum($result);
|
|
||||||
|
|
||||||
// convert to JSON response:
|
// convert to JSON response:
|
||||||
return response()->api($converted);
|
return response()->api(array_values($result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,8 @@ class ListController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
echo 'this needs move to Administration';
|
||||||
|
exit;
|
||||||
$collection = $this->repository->getActiveBudgets();
|
$collection = $this->repository->getActiveBudgets();
|
||||||
$total = $collection->count();
|
$total = $collection->count();
|
||||||
$collection->slice($this->pageSize * $this->parameters->get('page'), $this->pageSize);
|
$collection->slice($this->pageSize * $this->parameters->get('page'), $this->pageSize);
|
||||||
|
@ -41,8 +41,6 @@ class BalanceChartRequest extends FormRequest
|
|||||||
public function getAll(): array
|
public function getAll(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'start' => $this->getCarbonDate('start'),
|
|
||||||
'end' => $this->getCarbonDate('end'),
|
|
||||||
'accounts' => $this->getAccountList(),
|
'accounts' => $this->getAccountList(),
|
||||||
'period' => $this->string('period'),
|
'period' => $this->string('period'),
|
||||||
];
|
];
|
||||||
|
@ -39,6 +39,32 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
{
|
{
|
||||||
use AdministrationTrait;
|
use AdministrationTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct order of piggies in case of issues.
|
||||||
|
*/
|
||||||
|
public function correctOrder(): void
|
||||||
|
{
|
||||||
|
$set = $this->userGroup->bills()->orderBy('order', 'ASC')->get();
|
||||||
|
$current = 1;
|
||||||
|
foreach ($set as $bill) {
|
||||||
|
if ((int)$bill->order !== $current) {
|
||||||
|
$bill->order = $current;
|
||||||
|
$bill->save();
|
||||||
|
}
|
||||||
|
$current++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getBills(): Collection
|
||||||
|
{
|
||||||
|
return $this->userGroup->bills()
|
||||||
|
->orderBy('bills.name', 'ASC')
|
||||||
|
->get(['bills.*']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -109,7 +135,6 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
->get(['bills.*']);
|
->get(['bills.*']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
@ -32,11 +32,22 @@ use Illuminate\Support\Collection;
|
|||||||
*/
|
*/
|
||||||
interface BillRepositoryInterface
|
interface BillRepositoryInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* TODO duplicate of other repos
|
||||||
|
* Add correct order to bills.
|
||||||
|
*/
|
||||||
|
public function correctOrder(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection
|
* @return Collection
|
||||||
*/
|
*/
|
||||||
public function getActiveBills(): Collection;
|
public function getActiveBills(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function getBills(): Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Between start and end, tells you on which date(s) the bill is expected to hit.
|
* Between start and end, tells you on which date(s) the bill is expected to hit.
|
||||||
*
|
*
|
||||||
|
@ -42,6 +42,7 @@ trait ConvertsExchangeRates
|
|||||||
* @param array $set
|
* @param array $set
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public function cerChartSet(array $set): array
|
public function cerChartSet(array $set): array
|
||||||
{
|
{
|
||||||
@ -80,6 +81,7 @@ trait ConvertsExchangeRates
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getPreference(): void
|
private function getPreference(): void
|
||||||
{
|
{
|
||||||
@ -90,6 +92,7 @@ trait ConvertsExchangeRates
|
|||||||
* @param int $currencyId
|
* @param int $currencyId
|
||||||
*
|
*
|
||||||
* @return TransactionCurrency
|
* @return TransactionCurrency
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getCurrency(int $currencyId): TransactionCurrency
|
private function getCurrency(int $currencyId): TransactionCurrency
|
||||||
{
|
{
|
||||||
@ -107,6 +110,8 @@ trait ConvertsExchangeRates
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
|
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
|
||||||
{
|
{
|
||||||
@ -140,6 +145,8 @@ trait ConvertsExchangeRates
|
|||||||
* @param string $date
|
* @param string $date
|
||||||
*
|
*
|
||||||
* @return string|null
|
* @return string|null
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getFromDB(int $from, int $to, string $date): ?string
|
private function getFromDB(int $from, int $to, string $date): ?string
|
||||||
{
|
{
|
||||||
@ -178,6 +185,8 @@ trait ConvertsExchangeRates
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getEuroRate(TransactionCurrency $currency, Carbon $date): string
|
private function getEuroRate(TransactionCurrency $currency, Carbon $date): string
|
||||||
{
|
{
|
||||||
@ -212,6 +221,8 @@ trait ConvertsExchangeRates
|
|||||||
/**
|
/**
|
||||||
* @return int
|
* @return int
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function getEuroId(): int
|
private function getEuroId(): int
|
||||||
{
|
{
|
||||||
@ -293,6 +304,8 @@ trait ConvertsExchangeRates
|
|||||||
* @param Carbon|null $date
|
* @param Carbon|null $date
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
private function convertAmount(string $amount, TransactionCurrency $from, TransactionCurrency $to, ?Carbon $date = null): string
|
private function convertAmount(string $amount, TransactionCurrency $from, TransactionCurrency $to, ?Carbon $date = null): string
|
||||||
{
|
{
|
||||||
|
320
app/Transformers/V2/BillTransformer.php
Normal file
320
app/Transformers/V2/BillTransformer.php
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* BillTransformer.php
|
||||||
|
* Copyright (c) 2019 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Transformers\V2;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Carbon\CarbonInterface;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Models\Note;
|
||||||
|
use FireflyIII\Models\ObjectGroup;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BillTransformer
|
||||||
|
*/
|
||||||
|
class BillTransformer extends AbstractTransformer
|
||||||
|
{
|
||||||
|
private array $currencies;
|
||||||
|
private TransactionCurrency $default;
|
||||||
|
private array $groups;
|
||||||
|
private array $notes;
|
||||||
|
private array $paidDates;
|
||||||
|
private BillRepositoryInterface $repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BillTransformer constructor.
|
||||||
|
*
|
||||||
|
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function collectMetaData(Collection $objects): void
|
||||||
|
{
|
||||||
|
$currencies = [];
|
||||||
|
$bills = [];
|
||||||
|
$this->notes = [];
|
||||||
|
$this->groups = [];
|
||||||
|
$this->paidDates = [];
|
||||||
|
|
||||||
|
|
||||||
|
// start with currencies:
|
||||||
|
/** @var Bill $object */
|
||||||
|
foreach ($objects as $object) {
|
||||||
|
$id = (int)$object->transaction_currency_id;
|
||||||
|
$bills[] = (int)$object->id;
|
||||||
|
$currencies[$id] = $currencies[$id] ?? TransactionCurrency::find($id);
|
||||||
|
}
|
||||||
|
$this->currencies = $currencies;
|
||||||
|
|
||||||
|
// continue with notes
|
||||||
|
$notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get();
|
||||||
|
/** @var Note $note */
|
||||||
|
foreach ($notes as $note) {
|
||||||
|
$id = (int)$note->noteable_id;
|
||||||
|
$this->notes[$id] = $note;
|
||||||
|
}
|
||||||
|
// grab object groups:
|
||||||
|
$set = DB::table('object_groupables')
|
||||||
|
->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id')
|
||||||
|
->where('object_groupables.object_groupable_type', Bill::class)
|
||||||
|
->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']);
|
||||||
|
/** @var ObjectGroup $entry */
|
||||||
|
foreach ($set as $entry) {
|
||||||
|
$billId = (int)$entry->object_groupable_id;
|
||||||
|
$id = (int)$entry->object_group_id;
|
||||||
|
$order = (int)$entry->order;
|
||||||
|
$this->groups[$billId] = [
|
||||||
|
'object_group_id' => $id,
|
||||||
|
'object_group_title' => $entry->title,
|
||||||
|
'object_group_order' => $order,
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
||||||
|
$this->default = app('amount')->getDefaultCurrency();
|
||||||
|
|
||||||
|
// grab all paid dates:
|
||||||
|
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
|
||||||
|
$journals = TransactionJournal::whereIn('bill_id', $bills)
|
||||||
|
->where('date', '>=', $this->parameters->get('start'))
|
||||||
|
->where('date', '<=', $this->parameters->get('end'))
|
||||||
|
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']);
|
||||||
|
/** @var TransactionJournal $journal */
|
||||||
|
foreach ($journals as $journal) {
|
||||||
|
$billId = (int)$journal->bill_id;
|
||||||
|
$this->paidDates[$billId][] = [
|
||||||
|
'transaction_group_id' => (string)$journal->id,
|
||||||
|
'transaction_journal_id' => (string)$journal->transaction_group_id,
|
||||||
|
'date' => $journal->date->toAtomString(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the bill.
|
||||||
|
*
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function transform(Bill $bill): array
|
||||||
|
{
|
||||||
|
$paidData = $this->paidDates[(int)$bill->id] ?? [];
|
||||||
|
$nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []);
|
||||||
|
$payDates = $this->payDates($bill);
|
||||||
|
$currency = $this->currencies[(int)$bill->transaction_currency_id];
|
||||||
|
$group = $this->groups[(int)$bill->id] ?? null;
|
||||||
|
$nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates);
|
||||||
|
return [
|
||||||
|
'id' => (int)$bill->id,
|
||||||
|
'created_at' => $bill->created_at->toAtomString(),
|
||||||
|
'updated_at' => $bill->updated_at->toAtomString(),
|
||||||
|
'name' => $bill->name,
|
||||||
|
'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places),
|
||||||
|
'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places),
|
||||||
|
'currency_id' => (string)$bill->transaction_currency_id,
|
||||||
|
'currency_code' => $currency->code,
|
||||||
|
'currency_symbol' => $currency->symbol,
|
||||||
|
'currency_decimal_places' => (int)$currency->decimal_places,
|
||||||
|
'date' => $bill->date->toAtomString(),
|
||||||
|
'end_date' => $bill->end_date?->toAtomString(),
|
||||||
|
'extension_date' => $bill->extension_date?->toAtomString(),
|
||||||
|
'repeat_freq' => $bill->repeat_freq,
|
||||||
|
'skip' => (int)$bill->skip,
|
||||||
|
'active' => $bill->active,
|
||||||
|
'order' => (int)$bill->order,
|
||||||
|
'notes' => $this->notes[(int)$bill->id] ?? null,
|
||||||
|
'object_group_id' => $group ? $group['object_group_id'] : null,
|
||||||
|
'object_group_order' => $group ? $group['object_group_order'] : null,
|
||||||
|
'object_group_title' => $group ? $group['object_group_title'] : null,
|
||||||
|
'next_expected_match' => $nextExpectedMatch->toAtomString(),
|
||||||
|
'next_expected_match_diff' => $nextExpectedMatchDiff,
|
||||||
|
'pay_dates' => $payDates,
|
||||||
|
'paid_dates' => $paidData,
|
||||||
|
'links' => [
|
||||||
|
[
|
||||||
|
'rel' => 'self',
|
||||||
|
'uri' => sprintf('/bills/%d', $bill->id),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the data the bill was paid and predict the next expected match.
|
||||||
|
*
|
||||||
|
* @param Bill $bill
|
||||||
|
* @param array $dates
|
||||||
|
*
|
||||||
|
* @return Carbon
|
||||||
|
*/
|
||||||
|
protected function nextExpectedMatch(Bill $bill, array $dates): Carbon
|
||||||
|
{
|
||||||
|
// 2023-07-1 sub one day from the start date to fix a possible bug (see #7704)
|
||||||
|
// 2023-07-18 this particular date is used to search for the last paid date.
|
||||||
|
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
|
||||||
|
/** @var Carbon $start */
|
||||||
|
$start = clone $this->parameters->get('start');
|
||||||
|
$start->subDay();
|
||||||
|
|
||||||
|
$lastPaidDate = $this->lastPaidDate($dates, $start);
|
||||||
|
$nextMatch = clone $bill->date;
|
||||||
|
while ($nextMatch < $lastPaidDate) {
|
||||||
|
/*
|
||||||
|
* As long as this date is smaller than the last time the bill was paid, keep jumping ahead.
|
||||||
|
* For example: 1 jan, 1 feb, etc.
|
||||||
|
*/
|
||||||
|
$nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip);
|
||||||
|
}
|
||||||
|
if ($nextMatch->isSameDay($lastPaidDate)) {
|
||||||
|
/*
|
||||||
|
* Add another period because it's the same day as the last paid date.
|
||||||
|
*/
|
||||||
|
$nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip);
|
||||||
|
}
|
||||||
|
return $nextMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the latest date in the set, or start when set is empty.
|
||||||
|
*
|
||||||
|
* @param Collection $dates
|
||||||
|
* @param Carbon $default
|
||||||
|
*
|
||||||
|
* @return Carbon
|
||||||
|
*/
|
||||||
|
protected function lastPaidDate(array $dates, Carbon $default): Carbon
|
||||||
|
{
|
||||||
|
if (0 === count($dates)) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
$latest = $dates[0]['date'];
|
||||||
|
/** @var array $row */
|
||||||
|
foreach ($dates as $row) {
|
||||||
|
$carbon = new Carbon($row['date']);
|
||||||
|
if ($carbon->gte($latest)) {
|
||||||
|
$latest = $row['date'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Carbon($latest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Bill $bill
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function payDates(Bill $bill): array
|
||||||
|
{
|
||||||
|
//Log::debug(sprintf('Now in payDates() for bill #%d', $bill->id));
|
||||||
|
if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) {
|
||||||
|
//Log::debug('No start or end date, give empty array.');
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
$set = new Collection();
|
||||||
|
$currentStart = clone $this->parameters->get('start');
|
||||||
|
// 2023-06-23 subDay to fix 7655
|
||||||
|
$currentStart->subDay();
|
||||||
|
$loop = 0;
|
||||||
|
while ($currentStart <= $this->parameters->get('end')) {
|
||||||
|
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
|
||||||
|
// If nextExpectedMatch is after end, we continue:
|
||||||
|
if ($nextExpectedMatch > $this->parameters->get('end')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// add to set
|
||||||
|
$set->push(clone $nextExpectedMatch);
|
||||||
|
$nextExpectedMatch->addDay();
|
||||||
|
$currentStart = clone $nextExpectedMatch;
|
||||||
|
$loop++;
|
||||||
|
if ($loop > 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$simple = $set->map(
|
||||||
|
static function (Carbon $date) {
|
||||||
|
return $date->toAtomString();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $simple->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a bill and a date, this method will tell you at which moment this bill expects its next
|
||||||
|
* transaction. Whether or not it is there already, is not relevant.
|
||||||
|
*
|
||||||
|
* @param Bill $bill
|
||||||
|
* @param Carbon $date
|
||||||
|
*
|
||||||
|
* @return Carbon
|
||||||
|
*/
|
||||||
|
protected function nextDateMatch(Bill $bill, Carbon $date): Carbon
|
||||||
|
{
|
||||||
|
//Log::debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d')));
|
||||||
|
$start = clone $bill->date;
|
||||||
|
//Log::debug(sprintf('Bill start date is %s', $start->format('Y-m-d')));
|
||||||
|
while ($start < $date) {
|
||||||
|
$start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log::debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d')));
|
||||||
|
|
||||||
|
return $start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Carbon $next
|
||||||
|
* @param array $dates
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getNextExpectedMatchDiff(Carbon $next, array $dates): string
|
||||||
|
{
|
||||||
|
if ($next->isToday()) {
|
||||||
|
return trans('firefly.today');
|
||||||
|
}
|
||||||
|
$current = $dates[0] ?? null;
|
||||||
|
if (null === $current) {
|
||||||
|
return trans('firefly.not_expected_period');
|
||||||
|
}
|
||||||
|
$carbon = new Carbon($current);
|
||||||
|
return $carbon->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -124,15 +124,16 @@ Route::group(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 API route for bills.
|
* V2 API route for subscriptions.
|
||||||
*/
|
*/
|
||||||
Route::group(
|
Route::group(
|
||||||
[
|
[
|
||||||
'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Bill',
|
'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Bill',
|
||||||
'prefix' => 'v2/bills',
|
'prefix' => 'v2/subscriptions',
|
||||||
'as' => 'api.v2.bills.',
|
'as' => 'api.v2.subscriptions.',
|
||||||
],
|
],
|
||||||
static function () {
|
static function () {
|
||||||
|
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
|
||||||
Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
|
Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
|
||||||
Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
|
Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user