Compare commits

..

25 Commits

Author SHA1 Message Date
github-actions[bot]
a2127c382b Merge pull request #10061 from firefly-iii/release-1743405544
🤖 Automatically merge the PR into the develop branch.
2025-03-31 09:19:58 +02:00
JC5
e10d39c093 🤖 Auto commit for release 'develop' on 2025-03-31 2025-03-31 09:19:04 +02:00
James Cole
5beb476a28 Merge pull request #10060 from firefly-iii/add-missing-files
ok final one
2025-03-31 09:15:44 +02:00
Sander Dorigo
3a43ce6546 ok final one 2025-03-31 09:15:03 +02:00
James Cole
a3d3fe758b Merge pull request #10059 from firefly-iii/add-missing-files
add missing files
2025-03-31 09:12:25 +02:00
Sander Dorigo
93587cf1b6 add missing files 2025-03-31 09:11:17 +02:00
James Cole
003a30727f Merge pull request #10058 from firefly-iii/add-missing-files
Add new files
2025-03-31 09:06:39 +02:00
Sander Dorigo
27b662e2dc Add new files 2025-03-31 09:05:58 +02:00
James Cole
b974310798 Merge pull request #10057 from firefly-iii/add-missing-files
Add missing files
2025-03-31 09:02:58 +02:00
Sander Dorigo
e391725eed Add missing files 2025-03-31 09:01:29 +02:00
James Cole
b9afa908e3 Merge pull request #10045 from firefly-iii/v2-additions
Small additions
2025-03-29 07:58:56 +01:00
James Cole
6dcc78b9e5 Small additions 2025-03-29 07:58:45 +01:00
James Cole
7b0e7e8612 Merge pull request #10039 from ovv/ovv/cron-update-check
update check: consider cron succesfull when disabled or too frequent
2025-03-26 14:34:56 +01:00
=
1ece4abd9d update check: consider cron succesfull when disabled or too frequent
If the update check cron is disabled or run more often than once a week
consider the cron run succesfull.
2025-03-26 13:57:23 +01:00
James Cole
07c03b672b Merge pull request #10035 from firefly-iii/resurrect-v2
Resurrect v2
2025-03-25 17:31:50 +01:00
James Cole
27ea50ec16 Merge pull request #10034 from firefly-iii/main
Merge back into develop.
2025-03-25 17:29:52 +01:00
James Cole
e1195e6663 Fix JS views. 2025-03-25 17:28:12 +01:00
James Cole
faeb17f319 Recreate API endpoints. 2025-03-25 17:27:59 +01:00
James Cole
db91b1b127 Merge pull request #10033 from firefly-iii/dependabot/npm_and_yarn/npm_and_yarn-14f44f5325
Bump vite from 6.2.2 to 6.2.3 in the npm_and_yarn group across 1 directory
2025-03-25 16:35:57 +01:00
dependabot[bot]
b5b511c86b Bump vite in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 6.2.2 to 6.2.3
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 14:58:51 +00:00
James Cole
750019808c Finalise boxes. 2025-03-23 15:19:40 +01:00
James Cole
7f4510467f Convert subscription information to native. 2025-03-23 15:14:20 +01:00
James Cole
a46f8430df Account for renamed variable. 2025-03-23 15:04:38 +01:00
James Cole
5e1ecb2b11 Make sure the balance box converts to native. 2025-03-23 15:04:29 +01:00
James Cole
a3fcd636e7 Get v2 up and running (sort of) 2025-03-23 09:05:06 +01:00
62 changed files with 1990 additions and 768 deletions

View File

@@ -406,16 +406,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.73.1",
"version": "v3.74.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "ffcb8200a42045e65049af7910cfd022f631b064"
"reference": "6b7cb12a6cf592fd9a2b46a2d5d4c3b44003973d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/ffcb8200a42045e65049af7910cfd022f631b064",
"reference": "ffcb8200a42045e65049af7910cfd022f631b064",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/6b7cb12a6cf592fd9a2b46a2d5d4c3b44003973d",
"reference": "6b7cb12a6cf592fd9a2b46a2d5d4c3b44003973d",
"shasum": ""
},
"require": {
@@ -498,7 +498,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.73.1"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.74.0"
},
"funding": [
{
@@ -506,7 +506,7 @@
"type": "github"
}
],
"time": "2025-03-19T23:42:16+00:00"
"time": "2025-03-27T22:31:30+00:00"
},
{
"name": "psr/container",
@@ -1256,16 +1256,16 @@
},
{
"name": "symfony/console",
"version": "v7.2.1",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3"
"reference": "e51498ea18570c062e7df29d05a7003585b19b88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
"url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88",
"reference": "e51498ea18570c062e7df29d05a7003585b19b88",
"shasum": ""
},
"require": {
@@ -1329,7 +1329,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.2.1"
"source": "https://github.com/symfony/console/tree/v7.2.5"
},
"funding": [
{
@@ -1345,7 +1345,7 @@
"type": "tidelift"
}
],
"time": "2024-12-11T03:49:26+00:00"
"time": "2025-03-12T08:11:12+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -2243,16 +2243,16 @@
},
{
"name": "symfony/process",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf"
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
"url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d",
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d",
"shasum": ""
},
"require": {
@@ -2284,7 +2284,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.2.4"
"source": "https://github.com/symfony/process/tree/v7.2.5"
},
"funding": [
{
@@ -2300,7 +2300,7 @@
"type": "tidelift"
}
],
"time": "2025-02-05T08:33:46+00:00"
"time": "2025-03-13T12:21:46+00:00"
},
{
"name": "symfony/service-contracts",

View File

@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
## 2025
- =
- Lompi
- Jose Diaz-Gonzalez
- SoftBrix

View File

@@ -27,13 +27,16 @@ namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\ApiSupport;
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
@@ -43,8 +46,10 @@ use Illuminate\Http\JsonResponse;
class AccountController extends Controller
{
use ApiSupport;
use CollectsAccountsFromFilter;
private AccountRepositoryInterface $repository;
private ChartData $chartData;
/**
* AccountController constructor.
@@ -56,6 +61,7 @@ class AccountController extends Controller
function ($request, $next) {
/** @var User $user */
$user = auth()->user();
$this->chartData = new ChartData();
$this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUser($user);
@@ -64,6 +70,29 @@ class AccountController extends Controller
);
}
/**
* TODO fix documentation
*
* @throws FireflyException
*/
public function dashboard(ChartRequest $request): JsonResponse
{
$queryParameters = $request->getParameters();
$accounts = $this->getAccountList($queryParameters);
// move date to end of day
$queryParameters['start']->startOfDay();
$queryParameters['end']->endOfDay();
// loop each account, and collect info:
/** @var Account $account */
foreach ($accounts as $account) {
$this->renderAccountData($queryParameters, $account);
}
return response()->json($this->chartData->render());
}
/**
* This endpoint is documented at:
* https://api-docs.firefly-iii.org/?urls.primaryName=2.0.0%20(v1)#/charts/getChartAccountOverview
@@ -133,4 +162,45 @@ class AccountController extends Controller
return response()->json($chartData);
}
/**
* @throws FireflyException
*/
private function renderAccountData(array $params, Account $account): void
{
$currency = $this->repository->getAccountCurrency($account);
if (null === $currency) {
$currency = $this->default;
}
$currentSet = [
'label' => $account->name,
// the currency that belongs to the account.
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
// the default currency of the user (could be the same!)
'date' => $params['start']->toAtomString(),
'start' => $params['start']->toAtomString(),
'end' => $params['end']->toAtomString(),
'period' => '1D',
'entries' => [],
];
$currentStart = clone $params['start'];
$range = Steam::finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative);
$previous = array_values($range)[0]['balance'];
while ($currentStart <= $params['end']) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString();
$balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous;
$previous = $balance;
$currentStart->addDay();
$currentSet['entries'][$label] = $balance;
}
$this->chartData->add($currentSet);
}
}

View File

@@ -0,0 +1,260 @@
<?php
/*
* BudgetController.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/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Generic\DateRequest;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class BudgetController
*/
class BudgetController extends Controller
{
use CleansChartData;
use ValidatesUserGroupTrait;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
protected OperationsRepositoryInterface $opsRepository;
private BudgetLimitRepositoryInterface $blRepository;
private array $currencies = [];
private TransactionCurrency $currency;
private BudgetRepositoryInterface $repository;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->repository = app(BudgetRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup);
$this->opsRepository->setUserGroup($userGroup);
$this->blRepository->setUserGroup($userGroup);
return $next($request);
}
);
}
/**
* TODO see autocomplete/accountcontroller
*/
public function dashboard(DateRequest $request): JsonResponse
{
$params = $request->getAll();
/** @var Carbon $start */
$start = $params['start'];
/** @var Carbon $end */
$end = $params['end'];
// code from FrontpageChartGenerator, but not in separate class
$budgets = $this->repository->getActiveBudgets();
$data = [];
/** @var Budget $budget */
foreach ($budgets as $budget) {
// could return multiple arrays, so merge.
$data = array_merge($data, $this->processBudget($budget, $start, $end));
}
return response()->json($this->clean($data));
}
/**
* @throws FireflyException
*/
private function processBudget(Budget $budget, Carbon $start, Carbon $end): array
{
// get all limits:
$limits = $this->blRepository->getBudgetLimits($budget, $start, $end);
$rows = [];
// if no limits
if (0 === $limits->count()) {
// return as a single item in an array
$rows = $this->noBudgetLimits($budget, $start, $end);
}
if ($limits->count() > 0) {
$rows = $this->budgetLimits($budget, $limits);
}
// is always an array
$return = [];
foreach ($rows as $row) {
$current = [
'label' => $budget->name,
'currency_id' => (string) $row['currency_id'],
'currency_code' => $row['currency_code'],
'currency_name' => $row['currency_name'],
'currency_decimal_places' => $row['currency_decimal_places'],
'period' => null,
'start' => $row['start'],
'end' => $row['end'],
'entries' => [
'spent' => $row['spent'],
'left' => $row['left'],
'overspent' => $row['overspent'],
],
];
$return[] = $current;
}
return $return;
}
/**
* When no budget limits are present, the expenses of the whole period are collected and grouped.
* This is grouped per currency. Because there is no limit set, "left to spend" and "overspent" are empty.
*
* @throws FireflyException
*/
private function noBudgetLimits(Budget $budget, Carbon $start, Carbon $end): array
{
$spent = $this->opsRepository->listExpenses($start, $end, null, new Collection([$budget]));
return $this->processExpenses($budget->id, $spent, $start, $end);
}
/**
* Shared between the "noBudgetLimits" function and "processLimit". Will take a single set of expenses and return
* its info.
*
* @param array<int, array<int, string>> $array
*
* @throws FireflyException
*/
private function processExpenses(int $budgetId, array $array, Carbon $start, Carbon $end): array
{
$return = [];
/**
* This array contains the expenses in this budget. Grouped per currency.
* The grouping is on the main currency only.
*
* @var int $currencyId
* @var array $block
*/
foreach ($array as $currencyId => $block) {
$this->currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
$return[$currencyId] ??= [
'currency_id' => (string) $currencyId,
'currency_code' => $block['currency_code'],
'currency_name' => $block['currency_name'],
'currency_symbol' => $block['currency_symbol'],
'currency_decimal_places' => (int) $block['currency_decimal_places'],
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'spent' => '0',
'left' => '0',
'overspent' => '0',
];
$currentBudgetArray = $block['budgets'][$budgetId];
// var_dump($return);
/** @var array $journal */
foreach ($currentBudgetArray['transaction_journals'] as $journal) {
$return[$currencyId]['spent'] = bcadd($return[$currencyId]['spent'], $journal['amount']);
}
}
return $return;
}
/**
* Function that processes each budget limit (per budget).
*
* If you have a budget limit in EUR, only transactions in EUR will be considered.
* If you have a budget limit in GBP, only transactions in GBP will be considered.
*
* If you have a budget limit in EUR, and a transaction in GBP, it will not be considered for the EUR budget limit.
*
* @throws FireflyException
*/
private function budgetLimits(Budget $budget, Collection $limits): array
{
app('log')->debug(sprintf('Now in budgetLimits(#%d)', $budget->id));
$data = [];
/** @var BudgetLimit $limit */
foreach ($limits as $limit) {
$data = array_merge($data, $this->processLimit($budget, $limit));
}
return $data;
}
/**
* @throws FireflyException
*/
private function processLimit(Budget $budget, BudgetLimit $limit): array
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$end = clone $limit->end_date;
$end->endOfDay();
$spent = $this->opsRepository->listExpenses($limit->start_date, $end, null, new Collection([$budget]));
$limitCurrencyId = $limit->transaction_currency_id;
$filtered = [];
/** @var array $entry */
foreach ($spent as $currencyId => $entry) {
// only spent the entry where the entry's currency matches the budget limit's currency
// so $filtered will only have 1 or 0 entries
if ($entry['currency_id'] === $limitCurrencyId) {
$filtered[$currencyId] = $entry;
}
}
$result = $this->processExpenses($budget->id, $filtered, $limit->start_date, $end);
if (1 === count($result)) {
$compare = bccomp($limit->amount, app('steam')->positive($result[$limitCurrencyId]['spent']));
if (1 === $compare) {
// convert this amount into the native currency:
$result[$limitCurrencyId]['left'] = bcadd($limit->amount, $result[$limitCurrencyId]['spent']);
}
if ($compare <= 0) {
$result[$limitCurrencyId]['overspent'] = app('steam')->positive(bcadd($limit->amount, $result[$limitCurrencyId]['spent']));
}
}
return $result;
}
}

View File

@@ -0,0 +1,128 @@
<?php
/*
* CategoryController.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/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Api\V2\Request\Generic\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use Illuminate\Http\JsonResponse;
/**
* Class BudgetController
*/
class CategoryController extends Controller
{
use CleansChartData;
use ValidatesUserGroupTrait;
private AccountRepositoryInterface $accountRepos;
private CurrencyRepositoryInterface $currencyRepos;
public function __construct()
{
parent::__construct();
$this->middleware(
function ($request, $next) {
$this->accountRepos = app(AccountRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$userGroup = $this->validateUserGroup($request);
$this->accountRepos->setUserGroup($userGroup);
$this->currencyRepos->setUserGroup($userGroup);
return $next($request);
}
);
}
/**
* TODO may be worth to move to a handler but the data is simple enough.
* TODO see autoComplete/account controller
*
* @throws FireflyException
*
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
*/
public function dashboard(DateRequest $request): JsonResponse
{
/** @var Carbon $start */
$start = $this->parameters->get('start');
/** @var Carbon $end */
$end = $this->parameters->get('end');
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
$currencies = [];
$return = [];
// get journals for entire period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withAccountInformation();
$collector->setXorAccounts($accounts)->withCategoryInformation();
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]);
$journals = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
$currencies[$currencyId] = $currency;
$categoryName = null === $journal['category_name'] ? (string) trans('firefly.no_category') : $journal['category_name'];
$amount = app('steam')->positive($journal['amount']);
$key = sprintf('%s-%s', $categoryName, $currency->code);
// create arrays
$return[$key] ??= [
'label' => $categoryName,
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'period' => null,
'start' => $start->toAtomString(),
'end' => $end->toAtomString(),
'amount' => '0',
];
// add monies
$return[$key]['amount'] = bcadd($return[$key]['amount'], $amount);
}
$return = array_values($return);
// order by amount
usort($return, static function (array $a, array $b) {
return (float) $a['amount'] < (float) $b['amount'] ? 1 : -1;
});
return response()->json($this->clean($return));
}
}

View File

@@ -39,6 +39,8 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
@@ -100,7 +102,7 @@ class BasicController extends Controller
// balance information:
$balanceData = $this->getBalanceInformation($start, $end);
$billData = $this->getBillInformation($start, $end);
$billData = $this->getSubscriptionInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
$netWorthData = $this->getNetWorthInfo($end);
// $balanceData = [];
@@ -122,6 +124,7 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
Log::debug('getBalanceInformation');
// some config settings
$convertToNative = Amount::convertToNative();
$default = Amount::getNativeCurrency();
@@ -130,47 +133,110 @@ class BasicController extends Controller
$expenses = [];
$sums = [];
$return = [];
$currencies = [
$default->id => $default,
];
// collect income of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$summarizer = new TransactionSummarizer();
$set = $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value])->getExtractedJournals();
$incomes = $summarizer->groupByCurrencyId($set, 'positive', false);
$set = $collector->getExtractedJournals();
/** @var array $journal */
foreach ($set as $journal) {
$currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$incomes[$currencyId] ??= '0';
$incomes[$currencyId] = bcadd(
$incomes[$currencyId],
bcmul($amount, '-1')
);
$sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], bcmul($amount, '-1'));
}
// collect expenses of user.
// collect expenses of user using the new group collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$set = $collector->getExtractedJournals();
$set = $collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->getExtractedJournals();
$expenses = $summarizer->groupByCurrencyId($set, 'negative', false);
/** @var array $journal */
foreach ($set as $journal) {
$currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $amount);
// if convert to native, do so right now.
if ($convertToNative) {
$newExpenses = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
$newIncomes = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
$sums = [
$default->id => [
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'sum' => '0',
],
];
$converter = new ExchangeRateConverter();
// loop over income and expenses
foreach ([$expenses, $incomes] as $index => $array) {
// loop over either one.
foreach ($array as $entry) {
// if it is the native currency already.
if ($entry['currency_id'] === $default->id) {
$sums[$default->id]['sum'] = bcadd($entry['sum'], $sums[$default->id]['sum']);
// don't forget to add it to newExpenses and newIncome
if (0 === $index) {
$newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], $entry['sum']);
}
if (1 === $index) {
$newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], $entry['sum']);
}
continue;
}
$currencies[$entry['currency_id']] ??= $this->currencyRepos->find($entry['currency_id']);
$convertedSum = $converter->convert($currencies[$entry['currency_id']], $default, $start, $entry['sum']);
$sums[$default->id]['sum'] = bcadd($sums[$default->id]['sum'], $convertedSum);
if (0 === $index) {
$newExpenses[$default->id]['sum'] = bcadd($newExpenses[$default->id]['sum'], $convertedSum);
}
if (1 === $index) {
$newIncomes[$default->id]['sum'] = bcadd($newIncomes[$default->id]['sum'], $convertedSum);
}
}
}
$incomes = $newIncomes;
$expenses = $newExpenses;
}
if (!$convertToNative) {
foreach ([$expenses, $incomes] as $array) {
foreach ($array as $entry) {
$currencyId = $entry['currency_id'];
$sums[$currencyId] ??= [
'currency_id' => $entry['currency_id'],
'currency_code' => $entry['currency_code'],
'currency_symbol' => $entry['currency_symbol'],
'currency_decimal_places' => $entry['currency_decimal_places'],
'sum' => '0',
];
$sums[$currencyId]['sum'] = bcadd($sums[$currencyId]['sum'], $entry['sum']);
}
}
}
// format amounts:
$keys = array_keys($sums);
foreach ($keys as $currencyId) {
$currency = $this->currencyRepos->find($currencyId);
$currency = $currencies[$currencyId] ?? $this->currencyRepos->find($currencyId);
if (null === $currency) {
continue;
}
@@ -178,37 +244,37 @@ class BasicController extends Controller
$return[] = [
'key' => sprintf('balance-in-%s', $currency->code),
'title' => trans('firefly.box_balance_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $sums[$currencyId] ?? '0',
'monetary_value' => $sums[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId] ?? '0', false),
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId]['sum'] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false)
.' + '.app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false)
.' + '.app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
'title' => trans('firefly.box_spent_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $expenses[$currencyId] ?? '0',
'monetary_value' => $expenses[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId] ?? '0', false),
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
$return[] = [
'key' => sprintf('earned-in-%s', $currency->code),
'title' => trans('firefly.box_earned_in_currency', ['currency' => $currency->symbol]),
'monetary_value' => $incomes[$currencyId] ?? '0',
'monetary_value' => $incomes[$currencyId]['sum'] ?? '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId] ?? '0', false),
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
'local_icon' => 'balance-scale',
'sub_title' => '',
];
@@ -227,7 +293,7 @@ class BasicController extends Controller
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
'local_icon' => 'balance-scale',
'sub_title' => app('amount')->formatAnything($currency, '0', false)
.' + '.app('amount')->formatAnything($currency, '0', false),
.' + '.app('amount')->formatAnything($currency, '0', false),
];
$return[] = [
'key' => sprintf('spent-in-%s', $currency->code),
@@ -258,15 +324,72 @@ class BasicController extends Controller
return $return;
}
private function getBillInformation(Carbon $start, Carbon $end): array
private function getSubscriptionInformation(Carbon $start, Carbon $end): array
{
app('log')->debug(sprintf('Now in getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
Log::debug(sprintf('Now in getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
/*
* Since both this method and the chart use the exact same data, we can suffice
* with calling the one method in the bill repository that will get this amount.
*/
$paidAmount = $this->billRepository->sumPaidInRange($start, $end);
$unpaidAmount = $this->billRepository->sumUnpaidInRange($start, $end);
$currencies = [
$this->nativeCurrency->id => $this->nativeCurrency,
];
if ($this->convertToNative) {
$converter = new ExchangeRateConverter();
$newPaidAmount = [[
'id' => $this->nativeCurrency->id,
'name' => $this->nativeCurrency->name,
'symbol' => $this->nativeCurrency->symbol,
'code' => $this->nativeCurrency->code,
'decimal_places' => $this->nativeCurrency->decimal_places,
'sum' => '0',
]];
$newUnpaidAmount = [[
'id' => $this->nativeCurrency->id,
'name' => $this->nativeCurrency->name,
'symbol' => $this->nativeCurrency->symbol,
'code' => $this->nativeCurrency->code,
'decimal_places' => $this->nativeCurrency->decimal_places,
'sum' => '0',
]];
foreach ([$paidAmount, $unpaidAmount] as $index => $array) {
foreach ($array as $item) {
$currencyId = (int) $item['id'];
if (0 === $index) {
// paid amount
if ($currencyId === $this->nativeCurrency->id) {
$newPaidAmount[0]['sum'] = bcadd($newPaidAmount[0]['sum'], $item['sum']);
continue;
}
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$convertedAmount = $converter->convert($currencies[$currencyId], $this->nativeCurrency, $start, $item['sum']);
$newPaidAmount[0]['sum'] = bcadd($newPaidAmount[0]['sum'], $convertedAmount);
continue;
}
// unpaid amount
if ($currencyId === $this->nativeCurrency->id) {
$newUnpaidAmount[0]['sum'] = bcadd($newUnpaidAmount[0]['sum'], $item['sum']);
continue;
}
$currencies[$currencyId] ??= $this->currencyRepos->find($currencyId);
$convertedAmount = $converter->convert($currencies[$currencyId], $this->nativeCurrency, $start, $item['sum']);
$newUnpaidAmount[0]['sum'] = bcadd($newUnpaidAmount[0]['sum'], $convertedAmount);
}
}
$paidAmount = $newPaidAmount;
$unpaidAmount = $newUnpaidAmount;
}
// var_dump($paidAmount);
// var_dump($unpaidAmount);
// exit;
$return = [];
@@ -307,7 +430,7 @@ class BasicController extends Controller
'sub_title' => '',
];
}
app('log')->debug(sprintf('Done with getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
Log::debug(sprintf('Done with getBillInformation("%s", "%s")', $start->format('Y-m-d'), $end->format('Y-m-d-')));
if (0 === count($return)) {
$currency = $this->nativeCurrency;

View File

@@ -0,0 +1,91 @@
<?php
/*
* DashboardChartRequest.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/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests\Chart;
use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Validator;
/**
* Class ChartRequest
*/
class ChartRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
use ValidatesUserGroupTrait;
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
public function getParameters(): array
{
return [
'start' => $this->convertDateTime('start')?->startOfDay(),
'end' => $this->convertDateTime('end')?->endOfDay(),
'preselected' => $this->convertString('preselected', 'empty'),
'period' => $this->convertString('period', '1M'),
'accounts' => $this->arrayFromValue($this->get('accounts')),
];
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'start' => 'required|date|after:1900-01-01|before:2099-12-31|before_or_equal:end',
'end' => 'required|date|after:1900-01-01|before:2099-12-31|after_or_equal:start',
'preselected' => sprintf('nullable|in:%s', implode(',', config('firefly.preselected_accounts'))),
'period' => sprintf('nullable|in:%s', implode(',', config('firefly.valid_view_ranges'))),
'accounts.*' => 'exists:accounts,id',
];
}
public function withValidator(Validator $validator): void
{
$validator->after(
static function (Validator $validator): void {
// validate transaction query data.
$data = $validator->getData();
if (!array_key_exists('accounts', $data)) {
// $validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
return;
}
if (!is_array($data['accounts'])) {
$validator->errors()->add('accounts', trans('validation.filled', ['attribute' => 'accounts']));
}
}
);
if ($validator->fails()) {
Log::channel('audit')->error(sprintf('Validation errors in %s', __CLASS__), $validator->errors()->toArray());
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* DateRequest.php
* Copyright (c) 2021 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\Api\V1\Requests\Generic;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
* Request class for end points that require date parameters.
*
* Class DateRequest
*/
class DateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
/**
* Get all data from the request.
*/
public function getAll(): array
{
return [
'start' => $this->getCarbonDate('start')->startOfDay(),
'end' => $this->getCarbonDate('end')->endOfDay(),
];
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'start' => 'required|date|after:1900-01-01|before:2099-12-31',
'end' => 'required|date|after_or_equal:start|before:2099-12-31|after:1900-01-01',
];
}
}

View File

@@ -0,0 +1,59 @@
<?php
/*
* DateRequest.php
* Copyright (c) 2021 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\Api\V1\Requests\Generic;
use Carbon\Carbon;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
use Illuminate\Foundation\Http\FormRequest;
/**
* Request class for end points that require a date parameter.
*
* Class SingleDateRequest
*/
class SingleDateRequest extends FormRequest
{
use ChecksLogin;
use ConvertsDataTypes;
/**
* Get all data from the request.
*/
public function getDate(): Carbon
{
return $this->getCarbonDate('date');
}
/**
* The rules that the incoming request must be matched against.
*/
public function rules(): array
{
return [
'date' => 'required|date|after:1900-01-01|before:2099-12-31',
];
}
}

View File

@@ -138,7 +138,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface, U
->where('end_date', $end->format('Y-m-d'))->get()
;
Log::debug(sprintf('Found %d available budgets', $availableBudgets->count()));
Log::debug(sprintf('Found %d available budgets (already converted)', $availableBudgets->count()));
// use native amount if necessary?
$convertToNative = Amount::convertToNative($this->user);

View File

@@ -46,7 +46,7 @@ class ChartData
if (array_key_exists('native_currency_id', $data)) {
$data['native_currency_id'] = (string) $data['native_currency_id'];
}
$required = ['start', 'date', 'end', 'entries', 'native_entries'];
$required = ['start', 'date', 'end', 'entries'];
foreach ($required as $field) {
if (!array_key_exists($field, $data)) {
throw new FireflyException(sprintf('Data-set is missing the "%s"-variable.', $field));

View File

@@ -45,8 +45,8 @@ class UpdateCheckCronjob extends AbstractCronjob
Log::debug('Update check is not enabled.');
// get stuff from job:
$this->jobFired = false;
$this->jobErrored = true;
$this->jobSucceeded = false;
$this->jobErrored = false;
$this->jobSucceeded = true;
$this->message = 'The update check is not enabled.';
return;
@@ -61,8 +61,8 @@ class UpdateCheckCronjob extends AbstractCronjob
if ($diff < 604800 && false === $this->force) {
// get stuff from job:
$this->jobFired = false;
$this->jobErrored = true;
$this->jobSucceeded = false;
$this->jobErrored = false;
$this->jobSucceeded = true;
$this->message = sprintf('Checked for updates less than a week ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data));
return;

View File

@@ -49,7 +49,7 @@ class TransactionSummarizer
$this->convertToNative = Amount::convertToNative($user);
}
public function groupByCurrencyId(array $journals, string $method = 'negative'): array
public function groupByCurrencyId(array $journals, string $method = 'negative', bool $includeForeign = true): array
{
Log::debug(sprintf('Now in groupByCurrencyId(array, "%s")', $method));
$array = [];
@@ -94,10 +94,10 @@ class TransactionSummarizer
}
}
if (!$this->convertToNative) {
Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is %s)', $journal['transaction_journal_id'], $journal['foreign_currency_code']));
// use foreign amount?
$foreignCurrencyId = (int) $journal['foreign_currency_id'];
if (0 !== $foreignCurrencyId) {
Log::debug(sprintf('Journal #%d also includes foreign amount (foreign is "%s")', $journal['transaction_journal_id'], $journal['foreign_currency_code']));
$foreignCurrencyName = $journal['foreign_currency_name'];
$foreignCurrencySymbol = $journal['foreign_currency_symbol'];
$foreignCurrencyCode = $journal['foreign_currency_code'];
@@ -124,7 +124,7 @@ class TransactionSummarizer
}
// then process foreign amount, if it exists.
if (0 !== $foreignCurrencyId) {
if (0 !== $foreignCurrencyId && true === $includeForeign) {
$amount = (string) ($journal['foreign_amount'] ?? '0');
$array[$foreignCurrencyId] ??= [
'sum' => '0',

198
composer.lock generated
View File

@@ -1245,16 +1245,16 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.9.2",
"version": "7.9.3",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b"
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b",
"reference": "d281ed313b989f213357e3be1a179f02196ac99b",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77",
"shasum": ""
},
"require": {
@@ -1351,7 +1351,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.9.2"
"source": "https://github.com/guzzle/guzzle/tree/7.9.3"
},
"funding": [
{
@@ -1367,20 +1367,20 @@
"type": "tidelift"
}
],
"time": "2024-07-24T11:22:20+00:00"
"time": "2025-03-27T13:37:11+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "2.0.4",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455"
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
"reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455",
"url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c",
"reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c",
"shasum": ""
},
"require": {
@@ -1434,7 +1434,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.0.4"
"source": "https://github.com/guzzle/promises/tree/2.2.0"
},
"funding": [
{
@@ -1450,20 +1450,20 @@
"type": "tidelift"
}
],
"time": "2024-10-17T10:06:22+00:00"
"time": "2025-03-27T13:27:01+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.7.0",
"version": "2.7.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201"
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16",
"shasum": ""
},
"require": {
@@ -1550,7 +1550,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.7.0"
"source": "https://github.com/guzzle/psr7/tree/2.7.1"
},
"funding": [
{
@@ -1566,7 +1566,7 @@
"type": "tidelift"
}
],
"time": "2024-07-18T11:15:46+00:00"
"time": "2025-03-27T12:30:47+00:00"
},
{
"name": "guzzlehttp/uri-template",
@@ -2292,16 +2292,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.3",
"version": "v2.0.4",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "f379c13663245f7aa4512a7869f62eb14095f23f"
"reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/f379c13663245f7aa4512a7869f62eb14095f23f",
"reference": "f379c13663245f7aa4512a7869f62eb14095f23f",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841",
"reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841",
"shasum": ""
},
"require": {
@@ -2349,7 +2349,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2025-02-11T15:03:05+00:00"
"time": "2025-03-19T13:51:03+00:00"
},
{
"name": "laravel/slack-notification-channel",
@@ -2807,16 +2807,16 @@
},
{
"name": "league/csv",
"version": "9.22.0",
"version": "9.23.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/csv.git",
"reference": "afc109aa11f3086b8be8dfffa04ac31480b36b76"
"reference": "774008ad8a634448e4f8e288905e070e8b317ff3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/afc109aa11f3086b8be8dfffa04ac31480b36b76",
"reference": "afc109aa11f3086b8be8dfffa04ac31480b36b76",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/774008ad8a634448e4f8e288905e070e8b317ff3",
"reference": "774008ad8a634448e4f8e288905e070e8b317ff3",
"shasum": ""
},
"require": {
@@ -2894,7 +2894,7 @@
"type": "github"
}
],
"time": "2025-02-28T10:00:39+00:00"
"time": "2025-03-28T06:52:04+00:00"
},
{
"name": "league/event",
@@ -3608,16 +3608,16 @@
},
{
"name": "monolog/monolog",
"version": "3.8.1",
"version": "3.9.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4"
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
"reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6",
"reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6",
"shasum": ""
},
"require": {
@@ -3695,7 +3695,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/3.8.1"
"source": "https://github.com/Seldaek/monolog/tree/3.9.0"
},
"funding": [
{
@@ -3707,7 +3707,7 @@
"type": "tidelift"
}
],
"time": "2024-12-05T17:15:07+00:00"
"time": "2025-03-24T10:02:05+00:00"
},
{
"name": "nesbot/carbon",
@@ -3879,16 +3879,16 @@
},
{
"name": "nette/utils",
"version": "v4.0.5",
"version": "v4.0.6",
"source": {
"type": "git",
"url": "https://github.com/nette/utils.git",
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
"reference": "ce708655043c7050eb050df361c5e313cf708309"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
"url": "https://api.github.com/repos/nette/utils/zipball/ce708655043c7050eb050df361c5e313cf708309",
"reference": "ce708655043c7050eb050df361c5e313cf708309",
"shasum": ""
},
"require": {
@@ -3959,9 +3959,9 @@
],
"support": {
"issues": "https://github.com/nette/utils/issues",
"source": "https://github.com/nette/utils/tree/v4.0.5"
"source": "https://github.com/nette/utils/tree/v4.0.6"
},
"time": "2024-08-07T15:39:19+00:00"
"time": "2025-03-30T21:06:30+00:00"
},
{
"name": "nunomaduro/collision",
@@ -6360,16 +6360,16 @@
},
{
"name": "spatie/laravel-package-tools",
"version": "1.91.1",
"version": "1.92.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-package-tools.git",
"reference": "b0b509b9b01d77caa431ce9af3a706bc678e09c9"
"reference": "dd46cd0ed74015db28822d88ad2e667f4496a6f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/b0b509b9b01d77caa431ce9af3a706bc678e09c9",
"reference": "b0b509b9b01d77caa431ce9af3a706bc678e09c9",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/dd46cd0ed74015db28822d88ad2e667f4496a6f6",
"reference": "dd46cd0ed74015db28822d88ad2e667f4496a6f6",
"shasum": ""
},
"require": {
@@ -6408,7 +6408,7 @@
],
"support": {
"issues": "https://github.com/spatie/laravel-package-tools/issues",
"source": "https://github.com/spatie/laravel-package-tools/tree/1.91.1"
"source": "https://github.com/spatie/laravel-package-tools/tree/1.92.0"
},
"funding": [
{
@@ -6416,7 +6416,7 @@
"type": "github"
}
],
"time": "2025-03-21T09:50:49+00:00"
"time": "2025-03-27T08:34:10+00:00"
},
{
"name": "spatie/period",
@@ -6474,16 +6474,16 @@
},
{
"name": "symfony/cache",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache.git",
"reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7"
"reference": "9131e3018872d2ebb6fe8a9a4d6631273513d42c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache/zipball/d33cd9e14326e14a4145c21e600602eaf17cc9e7",
"reference": "d33cd9e14326e14a4145c21e600602eaf17cc9e7",
"url": "https://api.github.com/repos/symfony/cache/zipball/9131e3018872d2ebb6fe8a9a4d6631273513d42c",
"reference": "9131e3018872d2ebb6fe8a9a4d6631273513d42c",
"shasum": ""
},
"require": {
@@ -6552,7 +6552,7 @@
"psr6"
],
"support": {
"source": "https://github.com/symfony/cache/tree/v7.2.4"
"source": "https://github.com/symfony/cache/tree/v7.2.5"
},
"funding": [
{
@@ -6568,7 +6568,7 @@
"type": "tidelift"
}
],
"time": "2025-02-26T09:57:54+00:00"
"time": "2025-03-25T15:54:33+00:00"
},
{
"name": "symfony/cache-contracts",
@@ -6722,16 +6722,16 @@
},
{
"name": "symfony/console",
"version": "v7.2.1",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3"
"reference": "e51498ea18570c062e7df29d05a7003585b19b88"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
"url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88",
"reference": "e51498ea18570c062e7df29d05a7003585b19b88",
"shasum": ""
},
"require": {
@@ -6795,7 +6795,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.2.1"
"source": "https://github.com/symfony/console/tree/v7.2.5"
},
"funding": [
{
@@ -6811,7 +6811,7 @@
"type": "tidelift"
}
],
"time": "2024-12-11T03:49:26+00:00"
"time": "2025-03-12T08:11:12+00:00"
},
{
"name": "symfony/css-selector",
@@ -6947,16 +6947,16 @@
},
{
"name": "symfony/error-handler",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
"reference": "aabf79938aa795350c07ce6464dd1985607d95d5"
"reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/aabf79938aa795350c07ce6464dd1985607d95d5",
"reference": "aabf79938aa795350c07ce6464dd1985607d95d5",
"url": "https://api.github.com/repos/symfony/error-handler/zipball/102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b",
"reference": "102be5e6a8e4f4f3eb3149bcbfa33a80d1ee374b",
"shasum": ""
},
"require": {
@@ -7002,7 +7002,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/error-handler/tree/v7.2.4"
"source": "https://github.com/symfony/error-handler/tree/v7.2.5"
},
"funding": [
{
@@ -7018,7 +7018,7 @@
"type": "tidelift"
}
],
"time": "2025-02-02T20:27:07+00:00"
"time": "2025-03-03T07:12:39+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -7479,16 +7479,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.2.3",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0"
"reference": "371272aeb6286f8135e028ca535f8e4d6f114126"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0",
"reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/371272aeb6286f8135e028ca535f8e4d6f114126",
"reference": "371272aeb6286f8135e028ca535f8e4d6f114126",
"shasum": ""
},
"require": {
@@ -7537,7 +7537,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.2.3"
"source": "https://github.com/symfony/http-foundation/tree/v7.2.5"
},
"funding": [
{
@@ -7553,20 +7553,20 @@
"type": "tidelift"
}
],
"time": "2025-01-17T10:56:55+00:00"
"time": "2025-03-25T15:54:33+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "9f1103734c5789798fefb90e91de4586039003ed"
"reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/9f1103734c5789798fefb90e91de4586039003ed",
"reference": "9f1103734c5789798fefb90e91de4586039003ed",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/b1fe91bc1fa454a806d3f98db4ba826eb9941a54",
"reference": "b1fe91bc1fa454a806d3f98db4ba826eb9941a54",
"shasum": ""
},
"require": {
@@ -7651,7 +7651,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v7.2.4"
"source": "https://github.com/symfony/http-kernel/tree/v7.2.5"
},
"funding": [
{
@@ -7667,7 +7667,7 @@
"type": "tidelift"
}
],
"time": "2025-02-26T11:01:22+00:00"
"time": "2025-03-28T13:32:50+00:00"
},
{
"name": "symfony/mailer",
@@ -8607,16 +8607,16 @@
},
{
"name": "symfony/process",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf"
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
"url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d",
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d",
"shasum": ""
},
"require": {
@@ -8648,7 +8648,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.2.4"
"source": "https://github.com/symfony/process/tree/v7.2.5"
},
"funding": [
{
@@ -8664,7 +8664,7 @@
"type": "tidelift"
}
],
"time": "2025-02-05T08:33:46+00:00"
"time": "2025-03-13T12:21:46+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@@ -9332,16 +9332,16 @@
},
{
"name": "symfony/var-exporter",
"version": "v7.2.4",
"version": "v7.2.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
"reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a"
"reference": "c37b301818bd7288715d40de634f05781b686ace"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/4ede73aa7a73d81506002d2caadbbdad1ef5b69a",
"reference": "4ede73aa7a73d81506002d2caadbbdad1ef5b69a",
"url": "https://api.github.com/repos/symfony/var-exporter/zipball/c37b301818bd7288715d40de634f05781b686ace",
"reference": "c37b301818bd7288715d40de634f05781b686ace",
"shasum": ""
},
"require": {
@@ -9388,7 +9388,7 @@
"serialize"
],
"support": {
"source": "https://github.com/symfony/var-exporter/tree/v7.2.4"
"source": "https://github.com/symfony/var-exporter/tree/v7.2.5"
},
"funding": [
{
@@ -9404,7 +9404,7 @@
"type": "tidelift"
}
],
"time": "2025-02-13T10:27:23+00:00"
"time": "2025-03-13T12:21:46+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -10320,16 +10320,16 @@
},
{
"name": "composer/class-map-generator",
"version": "1.6.0",
"version": "1.6.1",
"source": {
"type": "git",
"url": "https://github.com/composer/class-map-generator.git",
"reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9"
"reference": "134b705ddb0025d397d8318a75825fe3c9d1da34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/ffe442c5974c44a9343e37a0abcb1cc37319f5b9",
"reference": "ffe442c5974c44a9343e37a0abcb1cc37319f5b9",
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/134b705ddb0025d397d8318a75825fe3c9d1da34",
"reference": "134b705ddb0025d397d8318a75825fe3c9d1da34",
"shasum": ""
},
"require": {
@@ -10373,7 +10373,7 @@
],
"support": {
"issues": "https://github.com/composer/class-map-generator/issues",
"source": "https://github.com/composer/class-map-generator/tree/1.6.0"
"source": "https://github.com/composer/class-map-generator/tree/1.6.1"
},
"funding": [
{
@@ -10389,7 +10389,7 @@
"type": "tidelift"
}
],
"time": "2025-02-05T10:05:34+00:00"
"time": "2025-03-24T13:50:44+00:00"
},
{
"name": "composer/pcre",
@@ -11222,16 +11222,16 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.10",
"version": "2.1.11",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f"
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f",
"reference": "051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30",
"shasum": ""
},
"require": {
@@ -11276,7 +11276,7 @@
"type": "github"
}
],
"time": "2025-03-23T14:57:55+00:00"
"time": "2025-03-24T13:45:00+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",

View File

@@ -78,7 +78,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2025-03-24',
'version' => 'develop/2025-03-31',
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25,
@@ -184,6 +184,7 @@ return [
'currencyPreference' => 'EUR',
'language' => 'en_US',
'locale' => 'equal',
'convertToNative' => false,
],
'default_currency' => 'EUR',
'default_language' => envNonEmpty('DEFAULT_LANGUAGE', 'en_US'),

595
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,26 +8,22 @@
"postinstall": "patch-package --error-on-fail"
},
"devDependencies": {
"axios": "^1.8.2",
"laravel-vite-plugin": "^1.0.5",
"patch-package": "^8.0.0",
"sass": "^1.78.0",
"axios": "^1",
"laravel-vite-plugin": "^1",
"patch-package": "^8",
"sass": "^1",
"vite": "^6",
"vite-plugin-manifest-sri": "^0.2.0"
},
"dependencies": {
"@ag-grid-community/client-side-row-model": "^32.0.2",
"@ag-grid-community/core": "^32.0.2",
"@ag-grid-community/infinite-row-model": "^32.0.2",
"@ag-grid-community/styles": "^33.0.2",
"@fortawesome/fontawesome-free": "^6.4.0",
"@popperjs/core": "^2.11.8",
"admin-lte": "^4.0.0-alpha3",
"admin-lte": "^4.0.0-beta3",
"alpinejs": "^3.13.7",
"bootstrap": "^5.3.0",
"bootstrap5-autocomplete": "^1.1.22",
"bootstrap5-tags": "^1.7",
"chart.js": "^4.4.0",
"bootstrap": "^5",
"bootstrap5-autocomplete": "^1",
"bootstrap5-tags": "^1",
"chart.js": "^4",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-chart-sankey": "^0.14.0",
"date-fns": "^4.0.0",

View File

@@ -0,0 +1,36 @@
/*
* overview.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import {format} from "date-fns";
export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/account/dashboard', {params: {fix: true, start: startStr, end: endStr}});
}
expense(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/account/expense-dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -0,0 +1,30 @@
/*
* overview.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import {format} from "date-fns";
export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/budget/dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -0,0 +1,30 @@
/*
* overview.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import {format} from "date-fns";
export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/category/dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../boot/axios";
import {api} from "../../../../boot/axios";
import format from "date-fns/format";
export default class Get {
@@ -37,6 +37,18 @@ export default class Get {
return api.get('/api/v1/accounts/' + identifier, {params: params});
}
/**
*
* @param identifier
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v1/accounts/' + identifier, {params: params});
}
/**
*
* @param identifier

View File

@@ -0,0 +1,36 @@
/*
* list.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import format from "date-fns/format";
export default class Put {
/**
*
* @param identifier
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
put(identifier, params) {
return api.put('/api/v2/accounts/' + identifier, params);
}
}

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../boot/axios";
import {api} from "../../../../boot/axios";
export default class Post {
post(fileName, attachableType, attachableId) {

View File

@@ -0,0 +1,35 @@
/*
* list.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import format from "date-fns/format";
export default class Get {
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/budgets', {params: params});
}
}

View File

@@ -0,0 +1,35 @@
/*
* list.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import format from "date-fns/format";
export default class Get {
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v2/currencies', {params: params});
}
}

View File

@@ -0,0 +1,35 @@
/*
* get.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Get {
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v1/piggy-banks', {params: params});
}
}

View File

@@ -0,0 +1,42 @@
/*
* get.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Get {
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v1/subscriptions', {params: params});
}
paid(params) {
return api.get('/api/v1/subscriptions/sum/paid', {params: params});
}
unpaid(params) {
return api.get('/api/v1/subscriptions/sum/unpaid', {params: params});
}
}

View File

@@ -0,0 +1,37 @@
/*
* get.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Get {
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
list(params) {
return api.get('/api/v1/transactions', {params: params});
}
show(id, params){
return api.get('/api/v1/transactions/' + id, {params: params});
}
}

View File

@@ -0,0 +1,28 @@
/*
* post.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = '/api/v1/transactions';
return api.post(url, submission);
}
}

View File

@@ -0,0 +1,28 @@
/*
* post.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v1/transactions/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -0,0 +1,45 @@
/*
* list.js
* Copyright (c) 2022 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/>.
*/
import {api} from "../../../../boot/axios";
import format from "date-fns/format";
export default class Get {
/**
*
* @param identifier
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
show(identifier, params) {
return api.get('/api/v2/user-groups/' + identifier, {params: params});
}
/**
*
* @param params
* @returns {Promise<AxiosResponse<any>>}
*/
index(params) {
return api.get('/api/v2/user-groups', {params: params});
}
}

View File

@@ -0,0 +1,33 @@
/*
* post.js
* Copyright (c) 2024 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/.
*/
import {api} from "../../../../boot/axios";
export default class Post {
post(submission) {
let url = './api/v2/user-groups';
return api.post(url, submission);
}
use(groupId) {
let url = './api/v2/user-groups/' + groupId + '/use';
return api.post(url, {});
}
}

View File

@@ -0,0 +1,28 @@
/*
* post.js
* 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/>.
*/
import {api} from "../../../../boot/axios";
export default class Put {
put(submission, params) {
let url = '/api/v2/user-groups/' + parseInt(params.id);
return api.put(url, submission);
}
}

View File

@@ -25,7 +25,7 @@ export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/account/dashboard', {params: {filter: {start: startStr, end: endStr}}});
return api.get('/api/v2/chart/account/dashboard', {params: {start: startStr, end: endStr}});
}
expense(start, end) {

View File

@@ -24,8 +24,8 @@ import i18next from "i18next";
import {format} from "date-fns";
import formatMoney from "../../util/format-money.js";
import Get from "../../api/v2/model/account/get.js";
import Put from "../../api/v2/model/account/put.js";
import Get from "../../api/v1/model/account/get.js";
import Put from "../../api/v1/model/account/put.js";
import AccountRenderer from "../../support/renderers/AccountRenderer.js";
import {showInternalsButton} from "../../support/page-settings/show-internals-button.js";
import {showWizardButton} from "../../support/page-settings/show-wizard-button.js";

View File

@@ -20,7 +20,7 @@
import '../../boot/bootstrap.js';
import dates from "../shared/dates.js";
import Post from "../../api/v2/model/user-group/post.js";
import Post from "../../api/v1/model/user-group/post.js";
import i18next from "i18next";

View File

@@ -20,10 +20,10 @@
import '../../boot/bootstrap.js';
import dates from "../shared/dates.js";
import Post from "../../api/v2/model/user-group/post.js";
import Post from "../../api/v1/model/user-group/post.js";
import i18next from "i18next";
import Get from "../../api/v2/model/user-group/get.js";
import Put from "../../api/v2/model/user-group/put.js";
import Get from "../../api/v1/model/user-group/get.js";
import Put from "../../api/v1/model/user-group/put.js";
let administrations = function () {

View File

@@ -23,11 +23,9 @@ import dates from "../shared/dates.js";
import i18next from "i18next";
import {format} from "date-fns";
import '@ag-grid-community/styles/ag-grid.css';
import '@ag-grid-community/styles/ag-theme-alpine.css';
import '../../css/grid-ff3-theme.css';
import Get from "../../api/v2/model/user-group/get.js";
import Post from "../../api/v2/model/user-group/post.js";
import Get from "../../api/v1/model/user-group/get.js";
import Post from "../../api/v1/model/user-group/post.js";
let index = function () {
return {

View File

@@ -20,9 +20,9 @@
import {getVariable} from "../../store/get-variable.js";
import {setVariable} from "../../store/set-variable.js";
import Dashboard from "../../api/v2/chart/account/dashboard.js";
import Dashboard from "../../api/v1/chart/account/dashboard.js";
import formatMoney from "../../util/format-money.js";
import Get from "../../api/v2/model/account/get.js";
import Get from "../../api/v1/model/account/get.js";
import {Chart} from 'chart.js';
import {getDefaultChartSettings} from "../../support/default-chart-settings.js";
import {getCacheKey} from "../../support/get-cache-key.js";
@@ -39,12 +39,12 @@ export default () => ({
loading: false,
loadingAccounts: false,
accountList: [],
autoConversion: false,
autoConversionAvailable: false,
convertToNative: false,
convertToNativeAvailable: false,
chartOptions: null,
switchAutoConversion() {
this.autoConversion = !this.autoConversion;
setVariable('autoConversion', this.autoConversion);
switchConvertToNative() {
this.convertToNative = !this.convertToNative;
setVariable('convertToNative', this.convertToNative);
},
localCacheKey(type) {
return 'ds_accounts_' + type;
@@ -90,18 +90,18 @@ export default () => ({
dataset.label = current.label;
// use the "native" currency code and use the "native_entries" as array
if (this.autoConversion) {
currencies.push(current.native_currency_code);
dataset.currency_code = current.native_currency_code;
collection = Object.values(current.native_entries);
yAxis = 'y' + current.native_currency_code;
}
if (!this.autoConversion) {
// if (this.convertToNative) {
// currencies.push(current.native_currency_code);
// dataset.currency_code = current.native_currency_code;
// collection = Object.values(current.native_entries);
// yAxis = 'y' + current.native_currency_code;
// }
// if (!this.convertToNative) {
yAxis = 'y' + current.currency_code;
dataset.currency_code = current.currency_code;
currencies.push(current.currency_code);
collection = Object.values(current.entries);
}
// }
dataset.yAxisID = yAxis;
dataset.data = collection;
@@ -217,12 +217,12 @@ export default () => ({
for (let iii = 0; iii < current.attributes.transactions.length; iii++) {
let currentTransaction = current.attributes.transactions[iii];
//console.log(currentTransaction);
let nativeAmountRaw = 'withdrawal' === currentTransaction.type ? parseFloat(currentTransaction.native_amount) * -1 : parseFloat(currentTransaction.native_amount);
//let nativeAmountRaw = 'withdrawal' === currentTransaction.type ? parseFloat(currentTransaction.native_amount) * -1 : parseFloat(currentTransaction.native_amount);
let amountRaw = 'withdrawal' === currentTransaction.type ? parseFloat(currentTransaction.amount) * -1 : parseFloat(currentTransaction.amount);
// if transfer and source is this account, multiply again
if('transfer' === currentTransaction.type && parseInt(currentTransaction.source_id) === accountId) { //
nativeAmountRaw = nativeAmountRaw * -1;
// nativeAmountRaw = nativeAmountRaw * -1;
amountRaw = amountRaw * -1;
}
@@ -232,8 +232,8 @@ export default () => ({
type: currentTransaction.type,
amount_raw: amountRaw,
amount: formatMoney(amountRaw, currentTransaction.currency_code),
native_amount_raw: nativeAmountRaw,
native_amount: formatMoney(nativeAmountRaw, currentTransaction.native_currency_code),
// native_amount_raw: nativeAmountRaw,
// native_amount: formatMoney(nativeAmountRaw, currentTransaction.native_currency_code),
});
}
groups.push(group);
@@ -244,7 +244,7 @@ export default () => ({
order: parent.attributes.order,
id: parent.id,
balance: parent.attributes.balance,
native_balance: parent.attributes.native_balance,
//native_balance: parent.attributes.native_balance,
groups: groups,
});
// console.log(parent.attributes);
@@ -266,12 +266,12 @@ export default () => ({
init() {
// console.log('accounts init');
Promise.all([getVariable('viewRange', '1M'), getVariable('autoConversion', false), getVariable('language', 'en_US'),
Promise.all([getVariable('viewRange', '1M'), getVariable('convertToNative', false), getVariable('language', 'en_US'),
getConfiguration('cer.enabled', false)
]).then((values) => {
//console.log('accounts after promises');
this.autoConversion = values[1] && values[3];
this.autoConversionAvailable = values[3];
this.convertToNative = values[1] && values[3];
this.convertToNativeAvailable = values[3];
afterPromises = true;
// main dashboard chart:
@@ -289,11 +289,11 @@ export default () => ({
this.loadChart();
this.loadAccounts();
});
window.store.observe('autoConversion', () => {
window.store.observe('convertToNative', () => {
if (!afterPromises) {
return;
}
// console.log('accounts observe autoconversion');
// console.log('accounts observe convertToNative');
this.loadChart();
this.loadAccounts();
});

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Summary from "../../api/v2/summary/index.js";
import Summary from "../../api/v1/summary/index.js";
import {format} from "date-fns";
import {getVariable} from "../../store/get-variable.js";
import formatMoney from "../../util/format-money.js";
@@ -31,7 +31,7 @@ export default () => ({
billBox: {paid: [], unpaid: []},
leftBox: {left: [], perDay: []},
netBox: {net: []},
autoConversion: false,
convertToNative: false,
loading: false,
boxData: null,
boxOptions: null,
@@ -42,8 +42,9 @@ export default () => ({
const boxesCacheKey = getCacheKey('ds_boxes_data', {start: start, end: end});
cleanupCache();
const cacheValid = window.store.get('cacheValid');
//const cacheValid = window.store.get('cacheValid');
let cachedData = window.store.get(boxesCacheKey);
const cacheValid = false; // force refresh
if (cacheValid && typeof cachedData !== 'undefined') {
this.boxData = cachedData;
@@ -75,114 +76,56 @@ export default () => ({
continue;
}
let key = current.key;
// native (auto conversion):
if (this.autoConversion) {
if (key.startsWith('balance-in-native')) {
this.balanceBox.amounts.push(formatMoney(current.value, current.currency_code));
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
continue;
}
// spent info is used in subtitle:
if (key.startsWith('spent-in-native')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// append the amount spent.
subtitles[current.currency_code] =
subtitles[current.currency_code] +
formatMoney(current.value, current.currency_code);
continue;
}
// earned info is used in subtitle:
if (key.startsWith('earned-in-native')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// prepend the amount earned.
subtitles[current.currency_code] =
formatMoney(current.value, current.currency_code) + ' + ' +
subtitles[current.currency_code];
continue;
}
if (key.startsWith('bills-unpaid-in-native')) {
this.billBox.unpaid.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('bills-paid-in-native')) {
this.billBox.paid.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('left-to-spend-in-native')) {
this.leftBox.left.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('left-per-day-to-spend-in-native')) { // per day
this.leftBox.perDay.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('net-worth-in-native')) {
this.netBox.net.push(formatMoney(current.value, current.currency_code));
continue;
}
console.log('NOT NATIVE');
if (key.startsWith('balance-in-')) {
this.balanceBox.amounts.push(formatMoney(current.monetary_value, current.currency_code));
continue;
}
// not native
if (!this.autoConversion && !key.endsWith('native')) {
if (key.startsWith('balance-in-')) {
this.balanceBox.amounts.push(formatMoney(current.value, current.currency_code));
continue;
// spent info is used in subtitle:
if (key.startsWith('spent-in-')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// spent info is used in subtitle:
if (key.startsWith('spent-in-')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// append the amount spent.
subtitles[current.currency_code] =
subtitles[current.currency_code] +
formatMoney(current.value, current.currency_code);
continue;
}
// earned info is used in subtitle:
if (key.startsWith('earned-in-')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// prepend the amount earned.
subtitles[current.currency_code] =
formatMoney(current.value, current.currency_code) + ' + ' +
subtitles[current.currency_code];
continue;
// append the amount spent.
subtitles[current.currency_code] =
subtitles[current.currency_code] +
formatMoney(current.monetary_value, current.currency_code);
continue;
}
// earned info is used in subtitle:
if (key.startsWith('earned-in-')) {
// prep subtitles (for later)
if (!subtitles.hasOwnProperty(current.currency_code)) {
subtitles[current.currency_code] = '';
}
// prepend the amount earned.
subtitles[current.currency_code] =
formatMoney(current.monetary_value, current.currency_code) + ' + ' +
subtitles[current.currency_code];
continue;
}
if (key.startsWith('bills-unpaid-in-')) {
this.billBox.unpaid.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('bills-paid-in-')) {
this.billBox.paid.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('left-to-spend-in-')) {
this.leftBox.left.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('left-per-day-to-spend-in-')) {
this.leftBox.perDay.push(formatMoney(current.value, current.currency_code));
continue;
}
if (key.startsWith('net-worth-in-')) {
this.netBox.net.push(formatMoney(current.value, current.currency_code));
if (key.startsWith('bills-unpaid-in-')) {
this.billBox.unpaid.push(formatMoney(current.monetary_value, current.currency_code));
continue;
}
if (key.startsWith('bills-paid-in-')) {
this.billBox.paid.push(formatMoney(current.monetary_value, current.currency_code));
continue;
}
if (key.startsWith('left-to-spend-in-')) {
this.leftBox.left.push(formatMoney(current.monetary_value, current.currency_code));
continue;
}
if (key.startsWith('left-per-day-to-spend-in-')) {
this.leftBox.perDay.push(formatMoney(current.monetary_value, current.currency_code));
continue;
}
if (key.startsWith('net-worth-in-')) {
this.netBox.net.push(formatMoney(current.monetary_value, current.currency_code));
}
}
}
}
@@ -210,10 +153,10 @@ export default () => ({
init() {
// console.log('boxes init');
// TODO can be replaced by "getVariables"
Promise.all([getVariable('viewRange'), getVariable('autoConversion', false)]).then((values) => {
Promise.all([getVariable('viewRange'), getVariable('convertToNative', false)]).then((values) => {
// console.log('boxes after promises');
afterPromises = true;
this.autoConversion = values[1];
this.convertToNative = values[1];
this.loadBoxes();
});
window.store.observe('end', () => {
@@ -224,12 +167,12 @@ export default () => ({
this.boxData = null;
this.loadBoxes();
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
// console.log('boxes observe autoConversion');
this.autoConversion = newValue;
// console.log('boxes observe convertToNative');
this.convertToNative = newValue;
this.loadBoxes();
});
},

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {getVariable} from "../../store/get-variable.js";
import Dashboard from "../../api/v2/chart/budget/dashboard.js";
import Dashboard from "../../api/v1/chart/budget/dashboard.js";
import {getDefaultChartSettings} from "../../support/default-chart-settings.js";
import formatMoney from "../../util/format-money.js";
import {Chart} from 'chart.js';
@@ -34,7 +34,7 @@ let afterPromises = false;
export default () => ({
loading: false,
autoConversion: false,
convertToNative: false,
loadChart() {
if (true === this.loading) {
return;
@@ -134,7 +134,7 @@ export default () => ({
// // convert to EUR yes no?
let label = current.label + ' (' + current.currency_code + ')';
options.data.labels.push(label);
if (this.autoConversion) {
if (this.convertToNative) {
currencies.push(current.native_currency_code);
// series 0: spent
options.data.datasets[0].data.push(parseFloat(current.native_entries.spent) * -1);
@@ -143,7 +143,7 @@ export default () => ({
// series 2: overspent
options.data.datasets[2].data.push(parseFloat(current.native_entries.overspent));
}
if (!this.autoConversion) {
if (!this.convertToNative) {
currencies.push(current.currency_code);
// series 0: spent
options.data.datasets[0].data.push(parseFloat(current.entries.spent) * -1);
@@ -172,8 +172,8 @@ export default () => ({
init() {
Promise.all([getVariable('autoConversion', false)]).then((values) => {
this.autoConversion = values[0];
Promise.all([getVariable('convertToNative', false)]).then((values) => {
this.convertToNative = values[0];
afterPromises = true;
if (false === this.loading) {
this.loadChart();
@@ -189,12 +189,12 @@ export default () => ({
this.loadChart();
}
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
// console.log('boxes observe autoConversion');
this.autoConversion = newValue;
// console.log('boxes observe convertToNative');
this.convertToNative = newValue;
if (false === this.loading) {
this.loadChart();
}

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {getVariable} from "../../store/get-variable.js";
import Dashboard from "../../api/v2/chart/category/dashboard.js";
import Dashboard from "../../api/v1/chart/category/dashboard.js";
import {getDefaultChartSettings} from "../../support/default-chart-settings.js";
import {Chart} from "chart.js";
import formatMoney from "../../util/format-money.js";
@@ -32,7 +32,7 @@ let afterPromises = false;
export default () => ({
loading: false,
autoConversion: false,
convertToNative: false,
generateOptions(data) {
currencies = [];
let options = getDefaultChartSettings('column');
@@ -44,7 +44,7 @@ export default () => ({
let current = data[i];
let code = current.currency_code;
// only use native code when doing auto conversion.
if (this.autoConversion) {
if (this.convertToNative) {
code = current.native_currency_code;
}
@@ -65,7 +65,7 @@ export default () => ({
let yAxis = 'y';
let current = data[i];
let code = current.currency_code;
if (this.autoConversion) {
if (this.convertToNative) {
code = current.native_currency_code;
}
@@ -77,7 +77,7 @@ export default () => ({
// this series' currency matches this column's currency.
amount = parseFloat(current.amount);
yAxis = 'y' + current.currency_code;
if (this.autoConversion) {
if (this.convertToNative) {
amount = parseFloat(current.native_amount);
yAxis = 'y' + current.native_currency_code;
}
@@ -183,8 +183,8 @@ export default () => ({
},
init() {
// console.log('categories init');
Promise.all([getVariable('autoConversion', false),]).then((values) => {
this.autoConversion = values[0];
Promise.all([getVariable('convertToNative', false),]).then((values) => {
this.convertToNative = values[0];
afterPromises = true;
this.loadChart();
});
@@ -195,11 +195,11 @@ export default () => ({
this.chartData = null;
this.loadChart();
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
this.autoConversion = newValue;
this.convertToNative = newValue;
this.loadChart();
});
},

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {getVariable} from "../../store/get-variable.js";
import Get from "../../api/v2/model/piggy-bank/get.js";
import Get from "../../api/v1/model/piggy-bank/get.js";
import {getCacheKey} from "../../support/get-cache-key.js";
import {format} from "date-fns";
import i18next from "i18next";
@@ -29,7 +29,7 @@ const PIGGY_CACHE_KEY = 'ds_pg_data';
export default () => ({
loading: false,
autoConversion: false,
convertToNative: false,
sankeyGrouping: 'account',
piggies: [],
getFreshData() {
@@ -96,14 +96,14 @@ export default () => ({
id: current.id,
name: current.attributes.name,
percentage: parseInt(current.attributes.percentage),
amount: this.autoConversion ? current.attributes.native_current_amount : current.attributes.current_amount,
amount: this.convertToNative ? current.attributes.native_current_amount : current.attributes.current_amount,
// left to save
left_to_save: this.autoConversion ? current.attributes.native_left_to_save : current.attributes.left_to_save,
left_to_save: this.convertToNative ? current.attributes.native_left_to_save : current.attributes.left_to_save,
// target amount
target_amount: this.autoConversion ? current.attributes.native_target_amount : current.attributes.target_amount,
target_amount: this.convertToNative ? current.attributes.native_target_amount : current.attributes.target_amount,
// save per month
save_per_month: this.autoConversion ? current.attributes.native_save_per_month : current.attributes.save_per_month,
currency_code: this.autoConversion ? current.attributes.native_currency_code : current.attributes.currency_code,
save_per_month: this.convertToNative ? current.attributes.native_save_per_month : current.attributes.save_per_month,
currency_code: this.convertToNative ? current.attributes.native_currency_code : current.attributes.currency_code,
};
dataSet[groupName].piggies.push(piggy);
@@ -129,10 +129,10 @@ export default () => ({
init() {
// console.log('piggies init');
apiData = [];
Promise.all([getVariable('autoConversion', false)]).then((values) => {
Promise.all([getVariable('convertToNative', false)]).then((values) => {
afterPromises = true;
this.autoConversion = values[0];
this.convertToNative = values[0];
this.loadPiggyBanks();
});
@@ -144,12 +144,12 @@ export default () => ({
apiData = [];
this.loadPiggyBanks();
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
// console.log('piggies observe autoConversion');
this.autoConversion = newValue;
// console.log('piggies observe convertToNative');
this.convertToNative = newValue;
this.loadPiggyBanks();
});
},

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {getVariable} from "../../store/get-variable.js";
import Get from "../../api/v2/model/transaction/get.js";
import Get from "../../api/v1/model/transaction/get.js";
import {getDefaultChartSettings} from "../../support/default-chart-settings.js";
import {Chart} from 'chart.js';
import {Flow, SankeyController} from 'chartjs-chart-sankey';
@@ -33,7 +33,7 @@ let currencies = [];
let afterPromises = false;
let chart = null;
let transactions = [];
let autoConversion = false;
let convertToNative = false;
let translations = {
category: null,
unknown_category: null,
@@ -83,37 +83,37 @@ function getObjectName(type, name, direction, code) {
// category 4x
if ('category' === type && null !== name && 'in' === direction) {
return translations.category + ' "' + name + '" (' + translations.in + (autoConversion ? ', ' + code + ')' : ')');
return translations.category + ' "' + name + '" (' + translations.in + (convertToNative ? ', ' + code + ')' : ')');
}
if ('category' === type && null === name && 'in' === direction) {
return translations.unknown_category + ' (' + translations.in + (autoConversion ? ', ' + code + ')' : ')');
return translations.unknown_category + ' (' + translations.in + (convertToNative ? ', ' + code + ')' : ')');
}
if ('category' === type && null !== name && 'out' === direction) {
return translations.category + ' "' + name + '" (' + translations.out + (autoConversion ? ', ' + code + ')' : ')');
return translations.category + ' "' + name + '" (' + translations.out + (convertToNative ? ', ' + code + ')' : ')');
}
if ('category' === type && null === name && 'out' === direction) {
return translations.unknown_category + ' (' + translations.out + (autoConversion ? ', ' + code + ')' : ')');
return translations.unknown_category + ' (' + translations.out + (convertToNative ? ', ' + code + ')' : ')');
}
// account 4x
if ('account' === type && null === name && 'in' === direction) {
return translations.unknown_source + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_source + (convertToNative ? ' (' + code + ')' : '');
}
if ('account' === type && null !== name && 'in' === direction) {
return translations.revenue_account + '"' + name + '"' + (autoConversion ? ' (' + code + ')' : '');
return translations.revenue_account + '"' + name + '"' + (convertToNative ? ' (' + code + ')' : '');
}
if ('account' === type && null === name && 'out' === direction) {
return translations.unknown_dest + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_dest + (convertToNative ? ' (' + code + ')' : '');
}
if ('account' === type && null !== name && 'out' === direction) {
return translations.expense_account + ' "' + name + '"' + (autoConversion ? ' (' + code + ')' : '');
return translations.expense_account + ' "' + name + '"' + (convertToNative ? ' (' + code + ')' : '');
}
// budget 2x
if ('budget' === type && null !== name) {
return translations.budget + ' "' + name + '"' + (autoConversion ? ' (' + code + ')' : '');
return translations.budget + ' "' + name + '"' + (convertToNative ? ' (' + code + ')' : '');
}
if ('budget' === type && null === name) {
return translations.unknown_budget + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_budget + (convertToNative ? ' (' + code + ')' : '');
}
console.error('Cannot handle: type:"' + type + '", dir: "' + direction + '"');
}
@@ -121,25 +121,25 @@ function getObjectName(type, name, direction, code) {
function getLabelName(type, name, code) {
// category
if ('category' === type && null !== name) {
return translations.category + ' "' + name + '"' + (autoConversion ? ' (' + code + ')' : '');
return translations.category + ' "' + name + '"' + (convertToNative ? ' (' + code + ')' : '');
}
if ('category' === type && null === name) {
return translations.unknown_category + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_category + (convertToNative ? ' (' + code + ')' : '');
}
// account
if ('account' === type && null === name) {
return translations.unknown_account + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_account + (convertToNative ? ' (' + code + ')' : '');
}
if ('account' === type && null !== name) {
return name + (autoConversion ? ' (' + code + ')' : '');
return name + (convertToNative ? ' (' + code + ')' : '');
}
// budget 2x
if ('budget' === type && null !== name) {
return translations.budget + ' "' + name + '"' + (autoConversion ? ' (' + code + ')' : '');
return translations.budget + ' "' + name + '"' + (convertToNative ? ' (' + code + ')' : '');
}
if ('budget' === type && null === name) {
return translations.unknown_budget + (autoConversion ? ' (' + code + ')' : '');
return translations.unknown_budget + (convertToNative ? ' (' + code + ')' : '');
}
console.error('Cannot handle: type:"' + type + '"');
}
@@ -147,7 +147,7 @@ function getLabelName(type, name, code) {
export default () => ({
loading: false,
autoConversion: false,
convertToNative: false,
generateOptions() {
let options = getDefaultChartSettings('sankey');
@@ -164,8 +164,8 @@ export default () => ({
if (group.attributes.transactions.hasOwnProperty(ii)) {
// properties of the transaction, used in the generation of the chart:
let transaction = group.attributes.transactions[ii];
let currencyCode = this.autoConversion ? transaction.native_currency_code : transaction.currency_code;
let amount = this.autoConversion ? parseFloat(transaction.native_amount) : parseFloat(transaction.amount);
let currencyCode = this.convertToNative ? transaction.native_currency_code : transaction.currency_code;
let amount = this.convertToNative ? parseFloat(transaction.native_amount) : parseFloat(transaction.amount);
let flowKey;
/*
@@ -194,7 +194,7 @@ export default () => ({
if (!amounts.hasOwnProperty(flowKey)) {
amounts[flowKey] = {
from: category,
to: translations.all_money + (this.autoConversion ? ' (' + currencyCode + ')' : ''),
to: translations.all_money + (this.convertToNative ? ' (' + currencyCode + ')' : ''),
amount: 0
};
}
@@ -214,7 +214,7 @@ export default () => ({
if (!amounts.hasOwnProperty(flowKey)) {
amounts[flowKey] = {
from: translations.all_money + (this.autoConversion ? ' (' + currencyCode + ')' : ''),
from: translations.all_money + (this.convertToNative ? ' (' + currencyCode + ')' : ''),
to: budget,
amount: 0
};
@@ -348,9 +348,9 @@ export default () => ({
init() {
// console.log('sankey init');
transactions = [];
Promise.all([getVariable('autoConversion', false)]).then((values) => {
this.autoConversion = values[0];
autoConversion = values[0];
Promise.all([getVariable('convertToNative', false)]).then((values) => {
this.convertToNative = values[0];
convertToNative = values[0];
// some translations:
translations.all_money = i18next.t('firefly.all_money');
translations.category = i18next.t('firefly.category');
@@ -378,12 +378,12 @@ export default () => ({
this.transactions = [];
this.loadChart();
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
// console.log('sankey observe autoConversion');
this.autoConversion = newValue;
// console.log('sankey observe convertToNative');
this.convertToNative = newValue;
this.loadChart();
});
},

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {getVariable} from "../../store/get-variable.js";
import Get from "../../api/v2/model/subscription/get.js";
import Get from "../../api/v1/model/subscription/get.js";
import {format} from "date-fns";
import {getCacheKey} from "../../support/get-cache-key.js";
import {Chart} from "chart.js";
@@ -66,10 +66,10 @@ function downloadSubscriptions(params) {
currency_code: current.attributes.currency_code,
// native amount
native_amount_min: current.attributes.native_amount_min,
native_amount_max: current.attributes.native_amount_max,
native_amount: (parseFloat(current.attributes.native_amount_max) + parseFloat(current.attributes.native_amount_min)) / 2,
native_currency_code: current.attributes.native_currency_code,
// native_amount_min: current.attributes.native_amount_min,
// native_amount_max: current.attributes.native_amount_max,
// native_amount: (parseFloat(current.attributes.native_amount_max) + parseFloat(current.attributes.native_amount_min)) / 2,
// native_currency_code: current.attributes.native_currency_code,
// paid transactions:
transactions: [],
@@ -79,8 +79,7 @@ function downloadSubscriptions(params) {
paid: current.attributes.paid_dates.length > 0,
};
// set variables
bill.expected_amount = params.autoConversion ? formatMoney(bill.native_amount, bill.native_currency_code) :
formatMoney(bill.amount, bill.currency_code);
bill.expected_amount = formatMoney(bill.amount, bill.currency_code);
bill.expected_times = i18next.t('firefly.subscr_expected_x_times', {
times: current.attributes.pay_dates.length,
amount: bill.expected_amount
@@ -92,22 +91,25 @@ function downloadSubscriptions(params) {
const currentPayment = current.attributes.paid_dates[iii];
let percentage = 100;
// math: -100+(paid/expected)*100
if (params.autoConversion) {
if (params.convertToNative) {
percentage = Math.round(-100 + ((parseFloat(currentPayment.native_amount) * -1) / parseFloat(bill.native_amount)) * 100);
}
if (!params.autoConversion) {
if (!params.convertToNative) {
percentage = Math.round(-100 + ((parseFloat(currentPayment.amount) * -1) / parseFloat(bill.amount)) * 100);
}
// TODO fix me
currentPayment.currency_code = 'EUR';
console.log('Currency code: "'+currentPayment+'"');
console.log(currentPayment);
let currentTransaction = {
amount: params.autoConversion ? formatMoney(currentPayment.native_amount, currentPayment.native_currency_code) : formatMoney(currentPayment.amount, currentPayment.currency_code),
amount: formatMoney(currentPayment.amount, currentPayment.currency_code),
percentage: percentage,
date: format(new Date(currentPayment.date), 'PP'),
foreign_amount: null,
};
if (null !== currentPayment.foreign_currency_code) {
currentTransaction.foreign_amount = params.autoConversion ? currentPayment.foreign_native_amount : currentPayment.foreign_amount;
currentTransaction.foreign_currency_code = params.autoConversion ? currentPayment.native_currency_code : currentPayment.foreign_currency_code;
currentTransaction.foreign_amount = currentPayment.foreign_amount;
currentTransaction.foreign_currency_code = currentPayment.foreign_currency_code;
}
bill.transactions.push(currentTransaction);
@@ -119,7 +121,7 @@ function downloadSubscriptions(params) {
// bill is unpaid, count the "pay_dates" and multiply with the "amount".
// since bill is unpaid, this can only be in currency amount and native currency amount.
const totalAmount = current.attributes.pay_dates.length * bill.amount;
const totalNativeAmount = current.attributes.pay_dates.length * bill.native_amount;
// const totalNativeAmount = current.attributes.pay_dates.length * bill.native_amount;
// for bill's currency
if (!subscriptionData[objectGroupId].payment_info.hasOwnProperty(bill.currency_code)) {
subscriptionData[objectGroupId].payment_info[bill.currency_code] = {
@@ -128,11 +130,11 @@ function downloadSubscriptions(params) {
unpaid: 0,
native_currency_code: bill.native_currency_code,
native_paid: 0,
native_unpaid: 0,
//native_unpaid: 0,
};
}
subscriptionData[objectGroupId].payment_info[bill.currency_code].unpaid += totalAmount;
subscriptionData[objectGroupId].payment_info[bill.currency_code].native_unpaid += totalNativeAmount;
//subscriptionData[objectGroupId].payment_info[bill.currency_code].native_unpaid += totalNativeAmount;
}
if (current.attributes.paid_dates.length > 0) {
@@ -149,15 +151,15 @@ function downloadSubscriptions(params) {
currency_code: bill.currency_code,
paid: 0,
unpaid: 0,
native_currency_code: bill.native_currency_code,
native_paid: 0,
native_unpaid: 0,
// native_currency_code: bill.native_currency_code,
// native_paid: 0,
//native_unpaid: 0,
};
}
const amount = parseFloat(currentJournal.amount) * -1;
const nativeAmount = parseFloat(currentJournal.native_amount) * -1;
// const nativeAmount = parseFloat(currentJournal.native_amount) * -1;
subscriptionData[objectGroupId].payment_info[currentJournal.currency_code].paid += amount;
subscriptionData[objectGroupId].payment_info[currentJournal.currency_code].native_paid += nativeAmount;
// subscriptionData[objectGroupId].payment_info[currentJournal.currency_code].native_paid += nativeAmount;
}
}
}
@@ -178,7 +180,7 @@ function downloadSubscriptions(params) {
export default () => ({
loading: false,
autoConversion: false,
convertToNative: false,
subscriptions: [],
startSubscriptions() {
this.loading = true;
@@ -198,7 +200,7 @@ export default () => ({
let params = {
start: format(start, 'y-MM-dd'),
end: format(end, 'y-MM-dd'),
autoConversion: this.autoConversion,
// convertToNative: this.convertToNative,
page: 1
};
downloadSubscriptions(params).then(() => {
@@ -226,9 +228,9 @@ export default () => ({
drawPieChart(groupId, groupTitle, data) {
let id = '#pie_' + groupId + '_' + data.currency_code;
//console.log(data);
const unpaidAmount = this.autoConversion ? data.native_unpaid : data.unpaid;
const paidAmount = this.autoConversion ? data.native_paid : data.paid;
const currencyCode = this.autoConversion ? data.native_currency_code : data.currency_code;
const unpaidAmount = data.unpaid;
const paidAmount = data.paid;
const currencyCode = data.currency_code;
const chartData = {
labels: [
i18next.t('firefly.paid'),
@@ -267,8 +269,8 @@ export default () => ({
},
init() {
Promise.all([getVariable('autoConversion', false)]).then((values) => {
this.autoConversion = values[0];
Promise.all([getVariable('convertToNative', false)]).then((values) => {
this.convertToNative = values[0];
afterPromises = true;
if (false === this.loading) {
@@ -285,11 +287,11 @@ export default () => ({
this.startSubscriptions();
}
});
window.store.observe('autoConversion', (newValue) => {
window.store.observe('convertToNative', (newValue) => {
if (!afterPromises) {
return;
}
this.autoConversion = newValue;
this.convertToNative = newValue;
if (false === this.loading) {
this.startSubscriptions();
}

View File

@@ -23,7 +23,7 @@ import dates from '../../pages/shared/dates.js';
import {createEmptySplit, defaultErrorSet} from "./shared/create-empty-split.js";
import {parseFromEntries} from "./shared/parse-from-entries.js";
import formatMoney from "../../util/format-money.js";
import Post from "../../api/v2/model/transaction/post.js";
import Post from "../../api/v1/model/transaction/post.js";
import {loadCurrencies} from "./shared/load-currencies.js";
import {loadBudgets} from "./shared/load-budgets.js";
import {loadPiggyBanks} from "./shared/load-piggy-banks.js";

View File

@@ -21,7 +21,7 @@
import '../../boot/bootstrap.js';
import dates from '../../pages/shared/dates.js';
import formatMoney from "../../util/format-money.js";
import Get from "../../api/v2/model/transaction/get.js";
import Get from "../../api/v1/model/transaction/get.js";
import {parseDownloadedSplits} from "./shared/parse-downloaded-splits.js";
import {addAutocomplete, getUrls} from "./shared/add-autocomplete.js";
import {
@@ -40,7 +40,7 @@ import Tags from "bootstrap5-tags";
import i18next from "i18next";
import {defaultErrorSet} from "./shared/create-empty-split.js";
import {parseFromEntries} from "./shared/parse-from-entries.js";
import Put from "../../api/v2/model/transaction/put.js";
import Put from "../../api/v1/model/transaction/put.js";
import {processAttachments} from "./shared/process-attachments.js";
import {spliceErrorsIntoTransactions} from "./shared/splice-errors-into-transactions.js";

View File

@@ -24,10 +24,8 @@ import i18next from "i18next";
import {format} from "date-fns";
import formatMoney from "../../util/format-money.js";
import '@ag-grid-community/styles/ag-grid.css';
import '@ag-grid-community/styles/ag-theme-alpine.css';
import '../../css/grid-ff3-theme.css';
import Get from "../../api/v2/model/transaction/get.js";
import Get from "../../api/v1/model/transaction/get.js";
let index = function () {
return {

View File

@@ -22,10 +22,10 @@ import Autocomplete from "bootstrap5-autocomplete";
export function getUrls() {
return {
description: '/api/v2/autocomplete/transaction-descriptions',
account: '/api/v2/autocomplete/accounts',
category: '/api/v2/autocomplete/categories',
tag: '/api/v2/autocomplete/tags',
description: '/api/v1/autocomplete/transaction-descriptions',
account: '/api/v1/autocomplete/accounts',
category: '/api/v1/autocomplete/categories',
tag: '/api/v1/autocomplete/tags',
}
}

View File

@@ -19,7 +19,7 @@
*/
import Get from "../../../api/v2/model/budget/get.js";
import Get from "../../../api/v1/model/budget/get.js";
export function loadBudgets() {
let params = {

View File

@@ -19,7 +19,7 @@
*/
import Get from "../../../api/v2/model/currency/get.js";
import Get from "../../../api/v1/model/currency/get.js";
export function loadCurrencies() {
let params = {

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Get from "../../../api/v2/model/piggy-bank/get.js";
import Get from "../../../api/v1/model/piggy-bank/get.js";
export function loadPiggyBanks() {
let params = {

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import SubscriptionGet from "../../../api/v2/model/subscription/get.js";
import SubscriptionGet from "../../../api/v1/model/subscription/get.js";
export function loadSubscriptions() {
let params = {

View File

@@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import AttachmentPost from "../../../api/v1/attachments/post.js";
import AttachmentPost from "../../../api/v1/model/attachment/post.js";
let uploadFiles = function (fileData) {
let count = fileData.length;

View File

@@ -21,7 +21,7 @@
import '../../boot/bootstrap.js';
import dates from "../shared/dates.js";
import i18next from "i18next";
import Get from "../../api/v2/model/transaction/get.js";
import Get from "../../api/v1/model/transaction/get.js";
import {parseDownloadedSplits} from "./shared/parse-downloaded-splits.js";
import {format} from "date-fns";
import formatMoney from "../../util/format-money.js";

View File

@@ -20,6 +20,6 @@
export function showInternalsButton() {
console.log('showInternalsButton');
// console.log('showInternalsButton');
document.querySelector('.toggle-page-internals').classList.remove('d-none');
}

View File

@@ -36,8 +36,8 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => {
let https = null;
if (command === 'serve') {
https = {
key: fs.readFileSync(`/sites/vm/tls-certificates/wildcard.sd.internal.key`),
cert: fs.readFileSync(`/sites/vm/tls-certificates/wildcard.sd.internal.crt`),
key: fs.readFileSync(`/vagrant/tls-certificates/wildcard.sd.internal.key`),
cert: fs.readFileSync(`/vagrant/tls-certificates/wildcard.sd.internal.crt`),
};
}
@@ -82,11 +82,13 @@ export default defineConfig(({command, mode, isSsrBuild, isPreview}) => {
server: {
origin: 'http://127.0.0.1:8000',
cors: true,
origin: 'https://firefly.sd.internal:5173',
watch: {
usePolling: true,
},
host: '10.0.0.15',
port: 5173,
host: true,
// hmr: {
// protocol: 'wss',
// },

View File

@@ -59,7 +59,7 @@
</div>
<div class="modal-body">
<p>
Body
Buttons.
</p>
</div>
<div class="modal-footer">

View File

@@ -9,17 +9,17 @@
<div class="card-body p-0" style="position: relative;height:400px;">
<canvas id="account-chart"></canvas>
</div>
<template x-if="autoConversionAvailable">
<template x-if="convertToNativeAvailable">
<div class="card-footer text-end">
<template x-if="autoConversion">
<button type="button" @click="switchAutoConversion"
<template x-if="convertToNative">
<button type="button" @click="switchConvertToNative"
class="btn btn-outline-info btm-sm">
<span
class="fa-solid fa-comments-dollar"></span> {{ __('firefly.disable_auto_convert') }}
</button>
</template>
<template x-if="!autoConversion">
<button type="button" @click="switchAutoConversion"
<template x-if="!convertToNative">
<button type="button" @click="switchConvertToNative"
class="btn btn-outline-info btm-sm">
<span
class="fa-solid fa-comments-dollar"></span> {{ __('firefly.enable_auto_convert') }}

View File

@@ -75,7 +75,7 @@
<ul class="list-unstyled list-no-margin">
<template x-for="transaction in group.transactions">
<li>
@include('partials.elements.amount', ['autoConversion' => true,'type' => 'transaction.type','amount' => 'transaction.amount','native' => 'transaction.native_amount'])
@include('partials.elements.amount', ['convertToNative' => true,'type' => 'transaction.type','amount' => 'transaction.amount','native' => 'transaction.native_amount'])
</li>
</template>
</ul>

View File

@@ -1,5 +1,5 @@
@if($autoConversion)
<template x-if="autoConversion">
@if($convertToNative)
<template x-if="convertToNative">
<span>
<template x-if="{{ $native }}_raw < 0">
<span class="text-danger">
@@ -20,7 +20,7 @@
</template>
</span>
</template>
<template x-if="!autoConversion">
<template x-if="!convertToNative">
<span>
<template x-if="{{ $amount }}_raw < 0">

View File

@@ -337,9 +337,33 @@ Route::group(
],
static function (): void {
Route::get('overview', ['uses' => 'AccountController@overview', 'as' => 'overview']);
Route::get('dashboard', ['uses' => 'AccountController@dashboard', 'as' => 'dashboard']);
}
);
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Chart',
'prefix' => 'v1/chart/budget',
'as' => 'api.v1.chart.budget.',
],
static function (): void {
Route::get('dashboard', ['uses' => 'BudgetController@dashboard', 'as' => 'dashboard']);
}
);
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Chart',
'prefix' => 'v1/chart/category',
'as' => 'api.v1.chart.category.',
],
static function (): void {
Route::get('dashboard', ['uses' => 'CategoryController@dashboard', 'as' => 'dashboard']);
}
);
// DATA ROUTES
// Export data API routes
Route::group(
@@ -351,6 +375,7 @@ Route::group(
static function (): void {
Route::get('accounts', ['uses' => 'ExportController@accounts', 'as' => 'accounts']);
Route::get('bills', ['uses' => 'ExportController@bills', 'as' => 'bills']);
Route::get('subscriptions', ['uses' => 'ExportController@bills', 'as' => 'subscriptions']);
Route::get('budgets', ['uses' => 'ExportController@budgets', 'as' => 'budgets']);
Route::get('categories', ['uses' => 'ExportController@categories', 'as' => 'categories']);
Route::get('piggy-banks', ['uses' => 'ExportController@piggyBanks', 'as' => 'piggy-banks']);
@@ -556,6 +581,24 @@ Route::group(
Route::get('{bill}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
Route::group(
[
'namespace' => 'FireflyIII\Api\V1\Controllers\Models\Bill',
'prefix' => 'v1/subscriptions',
'as' => 'api.v1.subscriptions.',
],
static function (): void {
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']);
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
Route::get('{bill}', ['uses' => 'ShowController@show', 'as' => 'show']);
Route::put('{bill}', ['uses' => 'UpdateController@update', 'as' => 'update']);
Route::delete('{bill}', ['uses' => 'DestroyController@destroy', 'as' => 'delete']);
Route::get('{bill}/attachments', ['uses' => 'ListController@attachments', 'as' => 'attachments']);
Route::get('{bill}/rules', ['uses' => 'ListController@rules', 'as' => 'rules']);
Route::get('{bill}/transactions', ['uses' => 'ListController@transactions', 'as' => 'transactions']);
}
);
// Available Budget API routes:
Route::group(